aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2022-10-28 14:14:05 -0400
committerbobzel <zzzman@gmail.com>2022-10-28 14:14:05 -0400
commit627c2aa72f61a888866840810a7d31cb0648437b (patch)
tree6901ba42fbefd78526a3a699a4b287632f30bbad
parent2fc88a931cb2fc3408297b000208990633445585 (diff)
fixed proxyfields to use cache if it's available which saves orders of magnitude opening up Files list. Fixed copying docs to appear in Files list. fixed undo for change perspective and header color
-rw-r--r--src/client/views/MainView.tsx7
-rw-r--r--src/client/views/collections/TreeView.tsx16
-rw-r--r--src/client/views/nodes/DocumentView.tsx74
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx12
-rw-r--r--src/fields/Doc.ts4
-rw-r--r--src/fields/List.ts8
-rw-r--r--src/fields/Proxy.ts79
7 files changed, 102 insertions, 98 deletions
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4dc1ebd99..052846e71 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -841,15 +841,12 @@ export class MainView extends React.Component {
}
expandFlyout = action((button: Doc) => {
- // bcz: What's going on here!?
+ // bcz: What's going on here!? --- may be fixed now, so commenting out ...
// Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab. All of the div below the CollectionFreeFormView's main div
// generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be.
// interactively adjusting the flyout fixes the problem. So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);)
this._leftMenuFlyoutWidth = this._leftMenuFlyoutWidth || 250;
- setTimeout(
- action(() => (this._leftMenuFlyoutWidth += 0.5)),
- 0
- );
+ //setTimeout(action(() => (this._leftMenuFlyoutWidth += 0.5)));
this._sidebarContent.proto = button.target as any;
this.LastButton = button;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 1e97eee37..ac8562d5a 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -55,7 +55,7 @@ export interface TreeViewProps {
indentDocument?: (editTitle: boolean) => void;
outdentDocument?: (editTitle: boolean) => void;
ScreenToLocalTransform: () => Transform;
- contextMenuItems: { script: ScriptField; filter: ScriptField; icon: string; label: string }[];
+ contextMenuItems?: { script: ScriptField; filter: ScriptField; icon: string; label: string }[];
dontRegisterView?: boolean;
styleProvider?: StyleProviderFunc | undefined;
treeViewHideHeaderFields: () => boolean;
@@ -302,7 +302,7 @@ export class TreeView extends React.Component<TreeViewProps> {
const pt = [e.clientX, e.clientY];
const rect = this._header.current!.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
+ const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocs?.length);
this._header.current!.className = 'treeView-header';
if (!this.props.treeView.outlineMode || DragManager.DocDragData?.treeViewDoc === this.props.treeView.rootDoc) {
if (inside) this._header.current!.className += ' treeView-header-inside';
@@ -362,7 +362,7 @@ export class TreeView extends React.Component<TreeViewProps> {
if (!this._header.current) return;
const rect = this._header.current.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
+ const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
if (de.complete.linkDragData) {
const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
const destDoc = this.doc;
@@ -401,7 +401,6 @@ export class TreeView extends React.Component<TreeViewProps> {
getTransform = () => this.refTransform(this._tref.current);
embeddedPanelWidth = () => this.props.panelWidth() / (this.props.treeView.props.NativeDimScaling?.() || 1);
embeddedPanelHeight = () => {
- console.log(this.props.treeView.rootDoc.title);
const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
return Math.min(
layoutDoc[HeightSym](),
@@ -509,7 +508,7 @@ export class TreeView extends React.Component<TreeViewProps> {
@computed get renderContent() {
TraceMobx();
const expandKey = this.treeViewExpandedView;
- const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } };
+ const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } }) ?? {};
if (['links', 'annotations', 'aliases', this.fieldKey].includes(expandKey)) {
const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
const sortKeys = Object.keys(sortings);
@@ -729,7 +728,7 @@ export class TreeView extends React.Component<TreeViewProps> {
const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, icon: 'copy', label: 'Open Alias' };
const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' };
return [
- ...this.props.contextMenuItems.filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)),
+ ...(this.props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)),
...(this.doc.isFolder
? folderOp
: Doc.IsSystem(this.doc)
@@ -831,7 +830,6 @@ export class TreeView extends React.Component<TreeViewProps> {
*/
@computed
get renderTitle() {
- //
TraceMobx();
const view = this._editTitle ? (
<EditableView
@@ -868,7 +866,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
})}
Document={this.doc}
- fitWidth={(doc: Doc) => true}
+ fitWidth={returnTrue}
DataDoc={undefined}
scriptContext={this}
hideDecorationTitle={this.props.treeView.outlineMode}
@@ -1035,7 +1033,7 @@ export class TreeView extends React.Component<TreeViewProps> {
const pt = [de.clientX, de.clientY];
const rect = this._header.current!.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
+ const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, false));
};
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5b26469ed..b5dde211b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,7 +1,7 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
import { Document } from '../../../fields/documentSchemas';
@@ -53,7 +53,6 @@ import { RadialMenu } from './RadialMenu';
import { ScriptingBox } from './ScriptingBox';
import { PresBox } from './trails/PresBox';
import React = require('react');
-import { CollectionTreeView } from '../collections/CollectionTreeView';
const { Howl } = require('howler');
interface Window {
@@ -1056,24 +1055,36 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
: false;
};
linkButtonInverseScaling = () => (this.props.NativeDimScaling?.() || 1) * this.props.DocumentView().screenToLocalTransform().Scale;
- @computed get contents() {
- TraceMobx();
+ get audioAnnoState() {
+ return this.dataDoc.audioAnnoState ?? 'stopped';
+ }
+ @computed get audioAnnoView() {
const audioAnnosCount = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null)?.length;
const audioTextAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations-text'], listSpec('string'), null);
- const audioView =
- (!this.props.isSelected() && !this._isHovering && this.dataDoc.audioAnnoState !== 2) || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (!audioAnnosCount && !this.dataDoc.audioAnnoState) ? null : (
- <Tooltip title={<div>{audioTextAnnos?.lastElement()}</div>}>
- <div className="documentView-audioBackground" onPointerDown={this.playAnnotation}>
- <FontAwesomeIcon
- className="documentView-audioFont"
- style={{ color: [audioAnnosCount ? 'blue' : 'gray', 'green', 'red'][NumCast(this.dataDoc.audioAnnoState)] }}
- icon={!audioAnnosCount ? 'microphone' : 'file-audio'}
- size="sm"
- />
- </div>
- </Tooltip>
- );
-
+ const audioIconColors = new Map<string, string>([
+ ['recording', 'red'],
+ ['playing', 'green'],
+ ['stopped', audioAnnosCount ? 'blue' : 'gray'],
+ ]);
+ return this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (!this.props.isSelected() && !this._isHovering && this.audioAnnoState !== 'recording') || (!audioAnnosCount && this.audioAnnoState === 'stopped') ? null : (
+ <Tooltip title={<div>{audioTextAnnos?.lastElement()}</div>}>
+ <div className="documentView-audioBackground" onPointerDown={this.playAnnotation}>
+ <FontAwesomeIcon className="documentView-audioFont" style={{ color: audioIconColors.get(StrCast(this.audioAnnoState)) }} icon={!audioAnnosCount ? 'microphone' : 'file-audio'} size="sm" />
+ </div>
+ </Tooltip>
+ );
+ }
+ @computed get linkCountView() {
+ return this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (!this.props.isSelected() && !this._isHovering) || this.hideLinkButton ? null : (
+ <DocumentLinksButton
+ View={this.props.DocumentView()}
+ scaling={this.linkButtonInverseScaling}
+ Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -36, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -32]}
+ />
+ );
+ }
+ @computed get contents() {
+ TraceMobx();
return (
<div
className="documentView-contentsView"
@@ -1095,10 +1106,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
width={this.props.PanelWidth()}
height={this.props.PanelHeight()}
onError={(e: any) => {
- setTimeout(
- action(() => (this._retryThumb = 0)),
- 0
- );
+ setTimeout(action(() => (this._retryThumb = 0)));
setTimeout(
action(() => (this._retryThumb = 1)),
150
@@ -1124,14 +1132,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
layoutKey={this.finalLayoutKey}
/>
{this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints}
- {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? null : (
- <DocumentLinksButton
- View={this.props.DocumentView()}
- scaling={this.linkButtonInverseScaling}
- Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -36, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -32]}
- />
- )}
- {audioView}
+ {this.linkCountView}
+ {this.audioAnnoView}
</div>
);
}
@@ -1210,7 +1212,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const self = this;
const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null);
const anno = audioAnnos.lastElement();
- if (anno instanceof AudioField && this.dataDoc.audioAnnoState === 0) {
+ if (anno instanceof AudioField && this.audioAnnoState === 'stopped') {
new Howl({
src: [anno.url.href],
format: ['mp3'],
@@ -1218,12 +1220,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
loop: false,
volume: 0.5,
onend: function () {
- runInAction(() => {
- self.dataDoc.audioAnnoState = 0;
- });
+ runInAction(() => (self.dataDoc.audioAnnoState = 'stopped'));
},
});
- this.dataDoc.audioAnnoState = 1;
+ this.dataDoc.audioAnnoState = 'playing';
}
};
@@ -1262,12 +1262,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
}
};
- runInAction(() => (dataDoc.audioAnnoState = 2));
+ runInAction(() => (dataDoc.audioAnnoState = 'recording'));
recorder.start();
setTimeout(() => {
recorder.stop();
DictationManager.Controls.stop(false);
- runInAction(() => (dataDoc.audioAnnoState = 0));
+ runInAction(() => (dataDoc.audioAnnoState = 'stopped'));
gumStream.getAudioTracks()[0].stop();
}, 5000);
});
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 883c4460b..2b83e9da8 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -198,7 +198,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
e.preventDefault();
}}
onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}>
- <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} onChange={action(e => setValue(Number(e.target.value)))} />
+ <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} onChange={undoBatch(action(e => setValue(Number(e.target.value))))} />
</div>
<div className={`button`} onClick={action(e => setValue(Number(checkResult) + 1))}>
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'plus'} />
@@ -221,7 +221,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
</div>
);
} else {
- return <div></div>;
+ return <div />;
}
}
@@ -303,7 +303,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined,
backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined,
}}
- onClick={() => script.script.run({ value }).result}>
+ onClick={undoBatch(() => script.script.run({ value }))}>
{value[0].toUpperCase() + value.slice(1)}
</div>
));
@@ -352,12 +352,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
}
colorPicker = (curColor: string) => {
- const change = (value: ColorState) => {
+ const change = (value: ColorState, ev: MouseEvent) => {
+ ev.preventDefault();
+ ev.stopPropagation();
const s = this.colorScript;
s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)();
};
const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent'];
- return <SketchPicker onChange={change} color={curColor} presetColors={presets} />;
+ return <SketchPicker onChange={change as any /* SketchPicker passes the mouse event to the callback, but the type system doesn't know that */} color={curColor} presetColors={presets} />;
};
/**
* Color button
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index fc43325fe..70cb10970 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -756,12 +756,13 @@ export namespace Doc {
}
cloneMap.set(doc[Id], copy);
}
+ Doc.IsPrototype(copy) && Doc.AddDocToList(Doc.MyFileOrphans, undefined, copy);
return copy;
}
export async function MakeClone(doc: Doc, dontCreate: boolean = false, asBranch = false, cloneMap: Map<string, Doc> = new Map()) {
const linkMap = new Map<Doc, Doc>();
const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = [];
- const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf', 'branches', 'branchOf'], dontCreate, asBranch);
+ const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf', 'branches', 'branchOf', 'context'], dontCreate, asBranch);
Array.from(linkMap.entries()).map((links: Doc[]) => LinkManager.Instance.addLink(links[1], true));
rtfMap.map(({ copy, key, field }) => {
const replacer = (match: any, attr: string, id: string, offset: any, string: any) => {
@@ -974,6 +975,7 @@ export namespace Doc {
if (retitle) {
copy.title = incrementTitleCopy(StrCast(copy.title));
}
+ Doc.IsPrototype(copy) && Doc.AddDocToList(Doc.MyFileOrphans, undefined, copy);
return copy;
}
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 5cc4ca543..edaa16003 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -278,7 +278,13 @@ class ListImpl<T extends Field> extends ObjectField {
const batchPromise = DocServer.GetRefFields(promised.map(p => p.promisedFieldId));
// as soon as we get the fields from the server, set all the list values in one
// action to generate one React dom update.
- batchPromise.then(pfields => promised.forEach(p => p.field.setValue(pfields[p.promisedFieldId])));
+ batchPromise.then(
+ action(pfields => {
+ for (let i = 0; i < promised.length; i++) {
+ promised[i].field.setValue(pfields[promised[i].promisedFieldId]);
+ }
+ })
+ );
// we also have to mark all lists items with this promise so that any calls to them
// will await the batch request and return the requested field value.
// This assumes the handler for 'promise' in the call above being invoked before the
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index 2c5f38818..e924ef7a3 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -1,28 +1,28 @@
-import { Deserializable } from "../client/util/SerializationHelper";
-import { FieldWaiting } from "./Doc";
-import { primitive, serializable } from "serializr";
-import { observable, action, runInAction } from "mobx";
-import { DocServer } from "../client/DocServer";
-import { RefField } from "./RefField";
-import { ObjectField } from "./ObjectField";
-import { Id, Copy, ToScriptString, ToString } from "./FieldSymbols";
-import { scriptingGlobal } from "../client/util/ScriptingGlobals";
-import { Plugins } from "./util";
+import { Deserializable } from '../client/util/SerializationHelper';
+import { FieldWaiting } from './Doc';
+import { primitive, serializable } from 'serializr';
+import { observable, action, runInAction } from 'mobx';
+import { DocServer } from '../client/DocServer';
+import { RefField } from './RefField';
+import { ObjectField } from './ObjectField';
+import { Id, Copy, ToScriptString, ToString } from './FieldSymbols';
+import { scriptingGlobal } from '../client/util/ScriptingGlobals';
+import { Plugins } from './util';
function deserializeProxy(field: any) {
if (!field.cache) {
field.cache = DocServer.GetCachedRefField(field.fieldId) as any;
}
}
-@Deserializable("proxy", deserializeProxy)
+@Deserializable('proxy', deserializeProxy)
export class ProxyField<T extends RefField> extends ObjectField {
constructor();
constructor(value: T);
constructor(fieldId: string);
constructor(value?: T | string) {
super();
- if (typeof value === "string") {
- this.cache = DocServer.GetCachedRefField(value) as any;
+ if (typeof value === 'string') {
+ //this.cache = DocServer.GetCachedRefField(value) as any;
this.fieldId = value;
} else if (value) {
this.cache = value;
@@ -36,16 +36,16 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
[ToScriptString]() {
- return "invalid";
+ return 'invalid';
}
[ToString]() {
- return "ProxyField";
+ return 'ProxyField';
}
@serializable(primitive())
- readonly fieldId: string = "";
+ readonly fieldId: string = '';
- // This getter/setter and nested object thing is
+ // This getter/setter and nested object thing is
// because mobx doesn't play well with observable proxies
@observable.ref
private _cache: { readonly field: T | undefined } = { field: undefined };
@@ -59,29 +59,29 @@ export class ProxyField<T extends RefField> extends ObjectField {
private failed = false;
private promise?: Promise<any>;
+ @action
value(): T | undefined | FieldWaiting<T> {
- if (this.cache) {
- return this.cache;
- }
- if (this.failed) {
- return undefined;
- }
- if (!this.promise) {
- const cached = DocServer.GetCachedRefField(this.fieldId);
- if (cached !== undefined) {
- runInAction(() => this.cache = cached as any);
- return cached as any;
- }
- this.promise = DocServer.GetRefField(this.fieldId).then(action((field: any) => {
- this.promise = undefined;
- this.cache = field;
- if (field === undefined) this.failed = true;
- return field;
- }));
+ if (this.cache) return this.cache;
+ if (this.failed) return undefined;
+
+ const cached = DocServer.GetCachedRefField(this.fieldId) as T;
+ if (cached !== undefined) {
+ this.cache = cached;
+ } else if (!this.promise) {
+ this.promise = DocServer.GetRefField(this.fieldId).then(
+ action((field: any) => {
+ this.promise = undefined;
+ this.cache = field;
+ this.failed = field === undefined;
+ return field;
+ })
+ ) as FieldWaiting<T>;
}
- return DocServer.GetCachedRefField(this.fieldId) ?? (this.promise as any);
+ return cached ?? this.promise;
+ }
+ promisedValue(): string {
+ return !this.cache && !this.failed && !this.promise && !DocServer.GetCachedRefField(this.fieldId) ? this.fieldId : '';
}
- promisedValue(): string { return !this.cache && !this.failed && !this.promise ? this.fieldId : ""; }
setPromise(promise: any) {
this.promise = promise;
}
@@ -127,6 +127,5 @@ function prefetchValue(proxy: PrefetchProxy<RefField>) {
}
@scriptingGlobal
-@Deserializable("prefetch_proxy", prefetchValue)
-export class PrefetchProxy<T extends RefField> extends ProxyField<T> {
-}
+@Deserializable('prefetch_proxy', prefetchValue)
+export class PrefetchProxy<T extends RefField> extends ProxyField<T> {}