aboutsummaryrefslogtreecommitdiff
path: root/src/fields
diff options
context:
space:
mode:
Diffstat (limited to 'src/fields')
-rw-r--r--src/fields/Doc.ts79
-rw-r--r--src/fields/InkField.ts33
-rw-r--r--src/fields/RichTextField.ts60
-rw-r--r--src/fields/RichTextUtils.ts8
-rw-r--r--src/fields/ScriptField.ts15
-rw-r--r--src/fields/Types.ts12
-rw-r--r--src/fields/documentSchemas.ts7
-rw-r--r--src/fields/util.ts1
8 files changed, 149 insertions, 66 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 3f8de2988..bb7df75fc 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -7,14 +7,14 @@ import { CollectionViewType, DocumentType } from '../client/documents/DocumentTy
import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper';
import { undoable, UndoManager } from '../client/util/UndoManager';
-import { ClientUtils, incrementTitleCopy } from '../ClientUtils';
+import { ClientUtils, imageUrlToBase64, incrementTitleCopy } from '../ClientUtils';
import {
AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, Animation, AudioPlay, Brushed, CachedUpdates, DirectLinks,
DocAcl, DocCss, DocData, DocLayout, DocViews, FieldKeys, FieldTuples, ForceServerWrite, Height, Highlight,
Initializing, Self, SelfProxy, TransitionTimer, UpdatingFromServer, Width
} from './DocSymbols'; // prettier-ignore
import { Copy, FieldChanged, HandleUpdate, Id, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
-import { InkTool } from './InkField';
+import { InkEraserTool, InkInkTool, InkTool } from './InkField';
import { List } from './List';
import { ObjectField, serverOpType } from './ObjectField';
import { PrefetchProxy, ProxyField } from './Proxy';
@@ -22,8 +22,9 @@ import { FieldId, RefField } from './RefField';
import { RichTextField } from './RichTextField';
import { listSpec } from './Schema';
import { ComputedField, ScriptField } from './ScriptField';
-import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor, toList } from './Types';
+import { BoolCast, Cast, DocCast, FieldValue, ImageCastWithSuffix, NumCast, RTFCast, StrCast, ToConstructor, toList } from './Types';
import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, setter, SharingPermissions } from './util';
+import { gptImageLabel } from '../client/apis/gpt/GPT';
export let ObjGetRefField: (id: string, force?: boolean) => Promise<Doc | undefined>;
export let ObjGetRefFields: (ids: string[]) => Promise<Map<string, Doc | undefined>>;
@@ -97,9 +98,8 @@ export namespace Field {
});
return script;
}
- export function toString(fieldIn: unknown) {
- const field = fieldIn as FieldType;
- if (typeof field === 'string' || typeof field === 'number' || typeof field === 'boolean') return String(field);
+ export function toString(field: FieldResult<FieldType> | FieldType | undefined) {
+ if (field instanceof Promise || typeof field === 'string' || typeof field === 'number' || typeof field === 'boolean') return String(field);
return field?.[ToString]?.() || '';
}
export function IsField(field: unknown): field is FieldType;
@@ -111,7 +111,7 @@ export namespace Field {
export function Copy(field: unknown) {
return field instanceof ObjectField ? ObjectField.MakeCopy(field) : (field as FieldType);
}
- UndoManager.SetFieldPrinter(toString);
+ UndoManager.SetFieldPrinter((val: unknown) => (IsField(val) ? toString(val) : ''));
}
export type FieldType = number | string | boolean | ObjectField | RefField;
export type Opt<T> = T | undefined;
@@ -237,7 +237,7 @@ export class Doc extends RefField {
public static get MyPublishedDocs() { return DocListCast(Doc.ActiveDashboard?.myPublishedDocs).concat(DocListCast(DocCast(Doc.UserDoc().myPublishedDocs)?.data)); } // prettier-ignore
public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); } // prettier-ignore
public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); } // prettier-ignore
- public static get MyAnnos() { return DocCast(Doc.UserDoc().myAnnos); } // prettier-ignore
+ public static get MyStickers() { return DocCast(Doc.UserDoc().myStickers); } // prettier-ignore
public static get MyLightboxDrawings() { return DocCast(Doc.UserDoc().myLightboxDrawings); } // prettier-ignore
public static get MyImports() { return DocCast(Doc.UserDoc().myImports); } // prettier-ignore
public static get MyFilesystem() { return DocCast(Doc.UserDoc().myFilesystem); } // prettier-ignore
@@ -254,6 +254,10 @@ export class Doc extends RefField {
public static set ActivePage(val) { Doc.UserDoc().activePage = val; } // prettier-ignore
public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; } // prettier-ignore
public static set ActiveTool(tool:InkTool){ Doc.UserDoc().activeTool = tool; } // prettier-ignore
+ public static get ActiveInk(): InkInkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkInkTool; } // prettier-ignore
+ public static set ActiveInk(tool:InkInkTool){ Doc.UserDoc().activeInkTool = tool; } // prettier-ignore
+ public static get ActiveEraser(): InkEraserTool { return StrCast(Doc.UserDoc().activeEraserTool, InkTool.None) as InkEraserTool; } // prettier-ignore
+ public static set ActiveEraser(tool:InkEraserTool){ Doc.UserDoc().activeEraserTool = tool; } // prettier-ignore
public static get ActivePresentation() { return DocCast(Doc.ActiveDashboard?.activePresentation) as Opt<Doc>; } // prettier-ignore
public static set ActivePresentation(val) { Doc.ActiveDashboard && (Doc.ActiveDashboard.activePresentation = val) } // prettier-ignore
public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); } // prettier-ignore
@@ -337,7 +341,6 @@ export class Doc extends RefField {
if (!id || forceSave) {
DocServer.CreateDocField(docProxy);
}
- // eslint-disable-next-line no-constructor-return
return docProxy; // need to return the proxy from the constructor so that all our added fields will get called
}
@@ -464,13 +467,7 @@ export class Doc extends RefField {
});
}
}
-
-// eslint-disable-next-line no-redeclare
export namespace Doc {
- export let SelectOnLoad: Doc | undefined;
- export function SetSelectOnLoad(doc: Doc | undefined) {
- SelectOnLoad = doc;
- }
export let DocDragDataName: string = '';
export function SetDocDragDataName(name: string) {
DocDragDataName = name;
@@ -661,7 +658,6 @@ export namespace Doc {
if (reversed) list.splice(0, 0, doc);
else list.push(doc);
} else {
- // eslint-disable-next-line no-lonely-if
if (reversed) list.splice(before ? list.length - ind + 1 : list.length - ind, 0, doc);
else list.splice(before ? ind : ind + 1, 0, doc);
}
@@ -965,6 +961,19 @@ export namespace Doc {
}
} else if (field instanceof PrefetchProxy) {
Doc.FindReferences(field.value, references, system);
+ } else if (field instanceof RichTextField) {
+ const re = /"docId"\s*:\s*"(.*?)"/g;
+ let match: string[] | null;
+ while ((match = re.exec(field.Data)) !== null) {
+ const urlString = match[1];
+ if (urlString) {
+ const rdoc = DocServer.GetCachedRefField(urlString);
+ if (rdoc) {
+ references.add(rdoc);
+ Doc.FindReferences(rdoc, references, system);
+ }
+ }
+ }
}
} else if (field instanceof Promise) {
// eslint-disable-next-line no-debugger
@@ -995,7 +1004,7 @@ export namespace Doc {
} else if (field instanceof ObjectField) {
const docAtKey = doc[key];
copy[key] =
- docAtKey instanceof Doc && key.includes('layout[')
+ docAtKey instanceof Doc && (key.includes('layout[') || docAtKey.cloneOnCopy)
? new ProxyField(Doc.MakeCopy(docAtKey)) // copy the expanded render template
: ObjectField.MakeCopy(field);
} else if (field instanceof Promise) {
@@ -1194,7 +1203,6 @@ export namespace Doc {
return Cast(Doc.UserDoc().myLinkDatabase, Doc, null);
}
export function SetUserDoc(doc: Doc) {
- // eslint-disable-next-line no-return-assign
return (manager._user_doc = doc);
}
@@ -1369,6 +1377,13 @@ export namespace Doc {
export const FilterAny = '--any--';
export const FilterNone = '--undefined--';
+ export function hasDocFilter(container: Opt<Doc>, key: string, value: string | undefined, fieldPrefix?: string) {
+ if (!container) return;
+ const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'childFilters';
+ const childFilters = StrListCast(container[filterField]);
+ return childFilters.some(filter => filter.split(FilterSep)[0] === key && (value === undefined || value === Doc.FilterAny || filter.split(FilterSep)[1] === value));
+ }
+
// filters document in a container collection:
// all documents with the specified value for the specified key are included/excluded
// based on the modifiers :"check", "x", undefined
@@ -1379,8 +1394,8 @@ export namespace Doc {
runInAction(() => {
for (let i = 0; i < childFilters.length; i++) {
const fields = childFilters[i].split(FilterSep); // split key:value:modifier
- if (fields[0] === key && (fields[1] === value?.toString() || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
- if (fields[2] === modifiers && modifiers && fields[1] === value?.toString()) {
+ if (fields[0] === key && (fields[1] === value?.toString() || value === Doc.FilterAny || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
+ if (fields[2] === modifiers && modifiers && (fields[1] === value?.toString() || value === Doc.FilterAny)) {
// eslint-disable-next-line no-param-reassign
if (toggle) modifiers = 'remove';
else return;
@@ -1449,6 +1464,25 @@ export namespace Doc {
});
}
+ /**
+ * text description of a Doc. RTF documents will have just their text and pdf documents will have the first 50 words.
+ * Image documents are converted to bse64 and gpt generates a description for them. all other documents use their title.
+ * @param doc
+ * @returns
+ */
+ export function getDescription(doc: Doc) {
+ const curDescription = StrCast(doc[DocData][Doc.LayoutFieldKey(doc) + '_description']);
+ const docText = (async (tdoc:Doc) => {
+ switch (tdoc.type) {
+ case DocumentType.PDF: return curDescription || StrCast(tdoc.text).split(/\s+/).slice(0, 50).join(' '); // first 50 words of pdf text
+ case DocumentType.IMG: return curDescription || imageUrlToBase64(ImageCastWithSuffix(Doc.LayoutField(tdoc), '_o') ?? '')
+ .then(hrefBase64 => gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.'));
+ case DocumentType.RTF: return RTFCast(tdoc[Doc.LayoutFieldKey(tdoc)]).Text;
+ default: return StrCast(tdoc.title).startsWith("Untitled") ? "" : StrCast(tdoc.title);
+ }}); // prettier-ignore
+ return docText(doc).then(text => (doc[DocData][Doc.LayoutFieldKey(doc) + '_description'] = text));
+ }
+
// prettier-ignore
export function toIcon(doc?: Doc, isOpen?: Opt<boolean>) {
if (isOpen) return doc?.isFolder ? 'chevron-down' : 'folder-open';
@@ -1483,7 +1517,6 @@ export namespace Doc {
case DocumentType.MAP: return 'map-marker-alt';
case DocumentType.DATAVIZ: return 'chart-bar';
case DocumentType.EQUATION: return 'calculator';
- case DocumentType.SIMULATION: return 'rocket';
case DocumentType.CONFIG: return 'folder-closed';
default:
}
@@ -1763,7 +1796,3 @@ ScriptingGlobals.add(function setDocRangeFilter(container: Doc, key: string, ran
ScriptingGlobals.add(function toJavascriptString(str: string) {
return Field.toJavascriptString(str as FieldType);
});
-// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function RtfField() {
- return RichTextField.RTFfield();
-});
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 17b99b033..d1dda106a 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -8,19 +8,32 @@ import { ObjectField } from './ObjectField';
// Helps keep track of the current ink tool in use.
export enum InkTool {
- None = 'none',
- Pen = 'pen',
- Highlighter = 'highlighter',
- StrokeEraser = 'strokeeraser',
- SegmentEraser = 'segmenteraser',
- RadiusEraser = 'radiuseraser',
- Eraser = 'eraser', // not a real tool, but a class of tools
- Stamp = 'stamp',
- Write = 'write',
- PresentationPin = 'presentationpin',
+ None = 'None',
+ Ink = 'Ink',
+ Eraser = 'Eraser', // not a real tool, but a class of tools
SmartDraw = 'smartdraw',
}
+export enum InkInkTool {
+ Pen = 'Pen',
+ Highlight = 'Highlight',
+ Write = 'Write',
+}
+
+export enum InkEraserTool {
+ Stroke = 'Stroke',
+ Segment = 'Segment',
+ Radius = 'Radius',
+}
+
+export enum InkProperty {
+ Mask = 'inkMask',
+ Labels = 'labels',
+ StrokeWidth = 'strokeWidth',
+ StrokeColor = 'strokeColor',
+ EraserWidth = ' eraserWidth',
+}
+
export type Segment = Array<Bezier>;
// Defines an ink as an array of points.
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index 3f13f7e6d..bcef1fefc 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -13,14 +13,19 @@ export class RichTextField extends ObjectField {
@serializable(true)
readonly Text: string;
- constructor(data: string, text: string = '') {
+ /**
+ * NOTE: if 'text' doesn't match the plain text of 'data', this can cause infinite loop problems or other artifacts when rendered.
+ * @param data this is the formatted text representation of the RTF
+ * @param text this is the plain text of whatever text is in the 'data'
+ */
+ constructor(data: string, text: string) {
super();
this.Data = data;
- this.Text = text;
+ this.Text = text; // ideally, we'd compute 'text' from 'data' by doing what Prosemirror does at run-time ... just need to figure out how to write that function accurately
}
Empty() {
- return !(this.Text || this.Data.toString().includes('dashField') || this.Data.toString().includes('align'));
+ return !(this.Text || this.Data.toString().includes('dashField') || this.Data.toString().includes('dashDoc') || this.Data.toString().includes('align'));
}
[Copy]() {
@@ -37,10 +42,51 @@ export class RichTextField extends ObjectField {
return this.Text;
}
- public static RTFfield() {
- return new RichTextField(
- `{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}`,
- ''
+ // AARAV ADD=
+ static ToProsemirrorDoc = (content: Record<string, unknown>[], selection: Record<string, unknown>) => ({
+ doc: {
+ type: 'doc',
+ content,
+ },
+ selection,
+ });
+
+ private static ToProsemirrorTextContent = (text: string, styles?: { bold?: boolean; italic?: boolean; fontSize?: number; color?: string }) => [
+ {
+ type: 'text',
+ marks: [
+ ...(styles?.bold ? [{ type: 'strong' }] : []),
+ ...(styles?.italic ? [{ type: 'em' }] : []),
+ ...(styles?.fontSize ? [{ type: 'pFontSize', attrs: { fontSize: `${styles.fontSize}px` } }] : []),
+ ...(styles?.color ? [{ type: 'pFontColor', attrs: { fontColor: styles.color } }] : []),
+ ],
+ text,
+ },
+ ];
+
+ private static ToProsemirrorDashDocContent = (docId: string) => [
+ {
+ type: 'dashDoc',
+ attrs: { width: '200px', height: '200px', title: 'dashDoc', float: 'unset', hidden: false, docId },
+ },
+ ];
+
+ private static ToProsemirror = (plaintext: string, imgDocId?: string, styles?: { bold?: boolean; italic?: boolean; fontSize?: number; color?: string }, selectBack?: number) =>
+ RichTextField.ToProsemirrorDoc(
+ plaintext
+ .split('\n')
+ .filter(text => (imgDocId ? text : true)) // if there's an image doc, we don't want it repeat for each paragraph -- assume there's only one paragraph with text in it
+ .map(text => ({
+ type: 'paragraph',
+ content: [
+ ...(text.length ? RichTextField.ToProsemirrorTextContent(text, styles) : []), // An empty paragraph gets treated as a line break
+ ...(imgDocId ? RichTextField.ToProsemirrorDashDocContent(imgDocId) : []),
+ ],
+ })),
+ { type: 'text', anchor: 2 + plaintext.length - (selectBack ?? 0), head: 2 + plaintext.length }
);
+
+ public static textToRtf(text: string, imgDocId?: string, styles?: { bold?: boolean; italic?: boolean; fontSize?: number; color?: string }, selectBack?: number) {
+ return new RichTextField(JSON.stringify(RichTextField.ToProsemirror(text, imgDocId, styles, selectBack)), text);
}
}
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index b3534dde7..42dd0d432 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -1,8 +1,7 @@
-/* eslint-disable @typescript-eslint/no-namespace */
/* eslint-disable no-await-in-loop */
/* eslint-disable no-use-before-define */
import { AssertionError } from 'assert';
-import * as Color from 'color';
+import Color from 'color';
import { docs_v1 as docsV1 } from 'googleapis';
import { Fragment, Mark, Node, Schema } from 'prosemirror-model';
import { sinkListItem } from 'prosemirror-schema-list';
@@ -175,7 +174,6 @@ export namespace RichTextUtils {
const indentMap = new Map<ListGroup, BulletPosition[]>();
let globalOffset = 0;
const nodes: Node[] = [];
- // eslint-disable-next-line no-restricted-syntax
for (const element of structured) {
if (Array.isArray(element)) {
lists.push(element);
@@ -374,11 +372,9 @@ export namespace RichTextUtils {
const marksToStyle = async (nodes: (Node | null)[]): Promise<docsV1.Schema$Request[]> => {
const requests: docsV1.Schema$Request[] = [];
let position = 1;
- // eslint-disable-next-line no-restricted-syntax
for (const node of nodes) {
if (node === null) {
position += 2;
- // eslint-disable-next-line no-continue
continue;
}
const { marks, attrs, nodeSize } = node;
@@ -390,9 +386,7 @@ export namespace RichTextUtils {
};
let mark: Mark;
const markMap = BuildMarkMap(marks);
- // eslint-disable-next-line no-restricted-syntax
for (const markName of Object.keys(schema.marks)) {
- // eslint-disable-next-line no-cond-assign
if (ignored.includes(markName) || !(mark = markMap[markName])) {
continue;
}
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 582c09f29..b294ee8c6 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -85,6 +85,7 @@ async function deserializeScript(scriptIn: ScriptField) {
}
@scriptingGlobal
+// eslint-disable-next-line no-use-before-define
@Deserializable('script', (obj: unknown) => deserializeScript(obj as ScriptField))
export class ScriptField extends ObjectField {
@serializable
@@ -137,7 +138,6 @@ export class ScriptField extends ObjectField {
[ToString]() {
return this.script.originalScript;
}
- // eslint-disable-next-line default-param-last
public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Doc | string | number | boolean }, transformer?: Transformer) {
return CompileScript(script, {
params: {
@@ -156,13 +156,11 @@ export class ScriptField extends ObjectField {
});
}
- // eslint-disable-next-line default-param-last
public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
return compiled.compiled ? new ScriptField(compiled) : undefined;
}
- // eslint-disable-next-line default-param-last
public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
const compiled = ScriptField.CompileScript(script, params, false, capturedVariables);
return compiled.compiled ? new ScriptField(compiled) : undefined;
@@ -186,6 +184,7 @@ export class ScriptField extends ObjectField {
}
@scriptingGlobal
+// eslint-disable-next-line no-use-before-define
@Deserializable('computed', (obj: unknown) => deserializeScript(obj as ComputedField))
export class ComputedField extends ScriptField {
static undefined = '__undefined';
@@ -229,7 +228,6 @@ export class ComputedField extends ScriptField {
[ToValue](doc: Doc) { return ComputedField.useComputed ? { value: this.value(doc) } : undefined; } // prettier-ignore
[Copy](): ObjectField { return new ComputedField(this.script, this.setterscript, this.rawscript); } // prettier-ignore
- // eslint-disable-next-line default-param-last
public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) {
const compiled = ScriptField.CompileScript(script, params, true, { value: '', ...capturedVariables });
const compiledsetter = setterscript ? ScriptField.CompileScript(setterscript, { ...params, value: 'any' }, false, capturedVariables) : undefined;
@@ -265,14 +263,9 @@ export class ComputedField extends ScriptField {
doc[`${fieldKey}_indexed`] = flist;
}
const getField = ScriptField.CompileScript(`getIndexVal(this['${fieldKey}_indexed'], this.${interpolatorKey})`, {}, true, {});
- const setField = ScriptField.CompileScript(
- `{setIndexVal (this['${fieldKey}_indexed'], this.${interpolatorKey}, value); console.log(this["${fieldKey}_indexed"][this.${interpolatorKey}],this.data,this["${fieldKey}_indexed"]))}`,
- { value: 'any' },
- false,
- {}
- );
+ const setField = ScriptField.CompileScript(`{setIndexVal(this['${fieldKey}_indexed'], this.${interpolatorKey}, value);}`, { value: 'any' }, false, {});
doc[fieldKey] = getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
- return doc[fieldKey];
+ return Field.Copy(doc[fieldKey]);
}
}
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index ef79f72e4..474882959 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -5,7 +5,7 @@ import { ProxyField } from './Proxy';
import { RefField } from './RefField';
import { RichTextField } from './RichTextField';
import { ScriptField } from './ScriptField';
-import { CsvField, ImageField, PdfField, WebField } from './URLField';
+import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
// eslint-disable-next-line no-use-before-define
export type ToConstructor<T extends FieldType> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: any[]) => T;
@@ -122,12 +122,22 @@ export function CsvCast(field: FieldResult, defaultVal: CsvField | null = null)
export function WebCast(field: FieldResult, defaultVal: WebField | null = null) {
return Cast(field, WebField, defaultVal);
}
+export function VideoCast(field: FieldResult, defaultVal: VideoField | null = null) {
+ return Cast(field, VideoField, defaultVal);
+}
+export function AudioCast(field: FieldResult, defaultVal: AudioField | null = null) {
+ return Cast(field, AudioField, defaultVal);
+}
export function PDFCast(field: FieldResult, defaultVal: PdfField | null = null) {
return Cast(field, PdfField, defaultVal);
}
export function ImageCast(field: FieldResult, defaultVal: ImageField | null = null) {
return Cast(field, ImageField, defaultVal);
}
+export function ImageCastWithSuffix(field: FieldResult, suffix: string, defaultVal: ImageField | null = null) {
+ const href = ImageCast(field, defaultVal)?.url.href;
+ return href ? `${href.split('.')[0]}${suffix}.${href.split('.')[1]}` : null;
+}
export function FieldValue<T extends FieldType, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
// eslint-disable-next-line no-redeclare
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 335683270..b27816f55 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -48,8 +48,8 @@ export const documentSchema = createSchema({
_columnsHideIfEmpty: 'boolean', // whether empty stacking view column headings should be hidden
// _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
// _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
- _text_fontSize: 'string',
- _text_fontFamily: 'string',
+ text_fontSize: 'string',
+ text_fontFamily: 'string',
_layout_sidebarWidthPercent: 'string', // percent of text window width taken up by sidebar
// appearance properties on the data document
@@ -70,7 +70,7 @@ export const documentSchema = createSchema({
stroke_startMarker: 'string',
stroke_endMarker: 'string',
stroke_dash: 'string',
- textTransform: 'string',
+ text_transform: 'string',
treeView_Open: 'boolean', // flag denoting whether the documents sub-tree (contents) is visible or hidden
treeView_ExpandedView: 'string', // name of field whose contents are being displayed as the document's subtree
treeView_ExpandedViewLock: 'boolean', // whether the expanded view can be changed
@@ -112,5 +112,4 @@ export const collectionSchema = createSchema({
});
export type Document = makeInterface<[typeof documentSchema]>;
-// eslint-disable-next-line no-redeclare
export const Document = makeInterface(documentSchema);
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 60eadcdfd..33764aca5 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -227,7 +227,6 @@ function getEffectiveAcl(target: Doc | ListImpl<FieldType>, user?: string): symb
* @param allowUpgrade whether permissions can be made less restrictive
* @param layoutOnly just sets the layout doc's ACL (unless the data doc has no entry for the ACL, in which case it will be set as well)
*/
-// eslint-disable-next-line default-param-last
export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, visited: Doc[] = [], allowUpgrade?: boolean, layoutOnly = false) {
const selfKey = `acl_${normalizeEmail(ClientUtils.CurrentUserEmail())}`;
if (!target || visited.includes(target) || key === selfKey) return;