1 Commits

30 changed files with 225 additions and 479 deletions

2
package-lock.json generated
View File

@@ -48981,7 +48981,7 @@
}, },
"packages/noodl-editor": { "packages/noodl-editor": {
"name": "fluxscape-editor", "name": "fluxscape-editor",
"version": "1.2.0", "version": "1.1.0",
"dependencies": { "dependencies": {
"@electron/remote": "^2.1.2", "@electron/remote": "^2.1.2",
"@jaames/iro": "^5.5.2", "@jaames/iro": "^5.5.2",

View File

@@ -4,7 +4,7 @@
"description": "Node-Based App Builder for Scalability & Rapid Development, a fork of Noodl", "description": "Node-Based App Builder for Scalability & Rapid Development, a fork of Noodl",
"author": "Fluxscape <contact@fluxscape.io>", "author": "Fluxscape <contact@fluxscape.io>",
"homepage": "https://fluxscape.io", "homepage": "https://fluxscape.io",
"version": "1.2.0", "version": "1.1.0",
"main": "src/main/main.js", "main": "src/main/main.js",
"scripts": { "scripts": {
"build": "npx ts-node -P ./tsconfig.build.json ./scripts/build.ts", "build": "npx ts-node -P ./tsconfig.build.json ./scripts/build.ts",

View File

@@ -6,7 +6,7 @@ import { EventDispatcher } from '../../shared/utils/EventDispatcher';
import ProjectModules from '../../shared/utils/projectmodules'; import ProjectModules from '../../shared/utils/projectmodules';
import { NodeLibrary } from './models/nodelibrary'; import { NodeLibrary } from './models/nodelibrary';
import { ProjectModel } from './models/projectmodel'; import { ProjectModel } from './models/projectmodel';
import { WarningRef, WarningsModel } from './models/warningsmodel'; import { WarningsModel } from './models/warningsmodel';
import DebugInspector from './utils/debuginspector'; import DebugInspector from './utils/debuginspector';
import * as Exporter from './utils/exporter'; import * as Exporter from './utils/exporter';
@@ -112,7 +112,7 @@ export class ViewerConnection extends Model {
} else if (request.cmd === 'showwarning' && request.type === 'viewer') { } else if (request.cmd === 'showwarning' && request.type === 'viewer') {
const content = JSON.parse(request.content); const content = JSON.parse(request.content);
if (ProjectModel.instance !== undefined) { if (ProjectModel.instance !== undefined) {
const ref: WarningRef = { const ref = {
component: ProjectModel.instance.getComponentWithName(content.componentName), component: ProjectModel.instance.getComponentWithName(content.componentName),
node: ProjectModel.instance.findNodeWithId(content.nodeId), node: ProjectModel.instance.findNodeWithId(content.nodeId),
key: content.key, key: content.key,
@@ -124,7 +124,7 @@ export class ViewerConnection extends Model {
} }
} else if (request.cmd === 'clearwarnings' && request.type === 'viewer') { } else if (request.cmd === 'clearwarnings' && request.type === 'viewer') {
const content = JSON.parse(request.content); const content = JSON.parse(request.content);
const ref: WarningRef = { const ref = {
component: ProjectModel.instance.getComponentWithName(content.componentName), component: ProjectModel.instance.getComponentWithName(content.componentName),
node: ProjectModel.instance.findNodeWithId(content.nodeId) node: ProjectModel.instance.findNodeWithId(content.nodeId)
}; };

View File

@@ -1,72 +0,0 @@
import { CloudService } from '@noodl-models/CloudServices';
import { ProjectModel } from '@noodl-models/projectmodel';
import { setCloudServices } from '@noodl-models/projectmodel.editor';
import { ToastLayer } from '../../../views/ToastLayer';
export type Command = {
kind: 'cloud-service';
use?: boolean;
name: string;
description?: string;
endpoint: string;
appId: string;
masterKey: string;
};
export async function execute(command: Command, _event: MessageEvent) {
const environment = await getOrCreate(command);
if (command.use) {
setCloudServices(ProjectModel.instance, environment);
}
}
async function getOrCreate(command: Command) {
const cloudServices = await CloudService.instance.backend.fetch();
const environment = cloudServices.find((c) => c.url === command.endpoint);
if (environment) {
if (
environment.name !== command.name ||
environment.description !== command.description ||
environment.appId !== command.appId ||
environment.masterKey !== command.masterKey
) {
await CloudService.instance.backend.update({
id: environment.id,
url: environment.url,
// Update the existing environment
name: command.name,
description: command.description,
appId: command.appId,
masterKey: command.masterKey
});
ToastLayer.showSuccess(`Cloud service "${command.name}" updated.`);
}
return {
id: environment.id,
endpoint: environment.url,
appId: command.appId
};
} else {
const create = await CloudService.instance.backend.create({
name: command.name,
description: command.description,
appId: command.appId,
url: command.endpoint,
masterKey: command.masterKey
});
ToastLayer.showSuccess(`Cloud service "${command.name}" added successfully.`);
return {
id: create.id,
endpoint: create.url,
appId: create.appId
};
}
}

View File

@@ -1,13 +1,11 @@
import * as CloudService from './commands/cloud-service';
import * as Notify from './commands/notify'; import * as Notify from './commands/notify';
import * as UploadAwsS3 from './commands/upload-aws-s3'; import * as UploadAwsS3 from './commands/upload-aws-s3';
type IFrameCommand = Notify.Command | UploadAwsS3.Command | CloudService.Command; type IFrameCommand = Notify.Command | UploadAwsS3.Command;
const commands: Record<IFrameCommand['kind'], (command: IFrameCommand, event: MessageEvent) => Promise<void>> = { const commands: Record<IFrameCommand['kind'], (command: IFrameCommand, event: MessageEvent) => Promise<void>> = {
notify: Notify.execute, notify: Notify.execute,
'upload-aws-s3': UploadAwsS3.execute, 'upload-aws-s3': UploadAwsS3.execute
'cloud-service': CloudService.execute
}; };
export function commandEventHandler(event: MessageEvent) { export function commandEventHandler(event: MessageEvent) {

View File

@@ -1,5 +1,5 @@
import { NodeGraphNode } from '@noodl-models/nodegraphmodel'; import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
import { type Warning, WarningsModel } from '@noodl-models/warningsmodel'; import { WarningsModel } from '@noodl-models/warningsmodel';
import { ProjectModel } from '../projectmodel'; import { ProjectModel } from '../projectmodel';
import NodeTypeAdapter from './NodeTypeAdapter'; import NodeTypeAdapter from './NodeTypeAdapter';
@@ -103,7 +103,7 @@ export class RouterNavigateAdapter extends NodeTypeAdapter {
const hasValidTarget = target && pageComponents.includes(target); const hasValidTarget = target && pageComponents.includes(target);
const warning: Warning = const warning =
hasValidTarget === false hasValidTarget === false
? { ? {
message: "The target page doesn't belong to the target router", message: "The target page doesn't belong to the target router",

View File

@@ -458,11 +458,11 @@ export class VariantModel extends Model {
} }
); );
} else if (c.type === 'defaultStateTransition') { } else if (c.type === 'defaultStateTransition') {
const state = var state =
this.getType().visualStates !== undefined this.getType().visualStates !== undefined
? this.getType().visualStates.find((s) => s.name === c.state) ? this.getType().visualStates.find((s) => s.name === c.state)
: undefined; : undefined;
const stateName = state !== undefined ? state.label : c.state; var stateName = state !== undefined ? state.label : c.state;
WarningsModel.instance.setWarning( WarningsModel.instance.setWarning(
{ key: 'variant-dst-conflict-' + this.name + '-' + this.getType().fullName + '-' + c.state }, { key: 'variant-dst-conflict-' + this.name + '-' + this.getType().fullName + '-' + c.state },

View File

@@ -72,7 +72,7 @@ export class NodeGraphNode extends Model {
metadata?: Record<string, any>; metadata?: Record<string, any>;
private _variant: TSFixme; private _variant: TSFixme;
_label: TSFixme; private _label: TSFixme;
private _type: TSFixme; private _type: TSFixme;
private _ports: TSFixme; private _ports: TSFixme;

View File

@@ -1167,28 +1167,6 @@ EventDispatcher.instance.on(
null null
); );
NodeLibrary.instance.on('libraryUpdated', () => {
const library = NodeLibrary.instance.library;
// Check if we have any data from the browser
if (Object.keys(library?.nodeIndex || {}).length > 0) {
const filepath = filesystem.join(ProjectModel.instance._retainedProjectDirectory, 'nodelibrary.json');
const compactLibrary = {
typecasts: library.typecasts,
dynamicports: library.dynamicports,
nodetypes: library.nodetypes,
// NOTE: Let's save the node index for now, most likely this is something we will just ignore.
nodeIndex: library.nodeIndex,
projectsettings: library.projectsettings
};
filesystem.writeJson(filepath, compactLibrary).then(() => {
console.log('saved nodelibrary.json');
});
}
});
function saveProject() { function saveProject() {
if (!ProjectModel.instance) return; if (!ProjectModel.instance) return;

View File

@@ -1,42 +1,9 @@
import { ComponentModel } from '@noodl-models/componentmodel';
import { ProjectModel } from '@noodl-models/projectmodel';
import { toArray } from 'underscore'; import { toArray } from 'underscore';
import type { ComponentModel } from '@noodl-models/componentmodel';
import Model from '../../../shared/model'; import Model from '../../../shared/model';
import type { NodeGraphNode } from './nodegraphmodel';
import { NodeLibrary } from './nodelibrary'; import { NodeLibrary } from './nodelibrary';
export type WarningLabel = 'warning' | 'error';
export type Warning =
| {
type?: string;
level?: WarningLabel;
message: string;
showGlobally?: boolean;
}
| {
type: 'conflict' | 'conflict-source-code';
level?: WarningLabel;
message: string;
showGlobally?: boolean;
conflictMetadata: {
parameter: string;
ours: string;
theirs: string;
};
onDismiss: () => void;
onUseTheirs: () => void;
};
export type WarningRef = {
key?: string;
component?: ComponentModel;
connection?: TSFixme;
node?: NodeGraphNode;
isFromViewer?: boolean;
};
/** /**
* The first level of the warnings object is component name * The first level of the warnings object is component name
* Second is the connection / node identifier * Second is the connection / node identifier
@@ -47,7 +14,7 @@ interface Warnings {
[node_connection_id: string]: { [node_connection_id: string]: {
[warningKey: string]: { [warningKey: string]: {
ref: TSFixme; ref: TSFixme;
warning: Warning; warning: TSFixme;
}; };
}; };
}; };
@@ -57,7 +24,7 @@ export class WarningsModel extends Model {
public static instance = new WarningsModel(); public static instance = new WarningsModel();
private warnings: Warnings = {}; private warnings: Warnings = {};
private notifyChangedScheduled = false; private notifyChangedScheduled: boolean = false;
constructor() { constructor() {
super(); super();
@@ -68,12 +35,10 @@ export class WarningsModel extends Model {
}); });
} }
public setWarning(ref: WarningRef, warning: Warning) { public setWarning(ref, warning) {
const w = this.getWarningsForRef(ref, warning !== undefined); var w = this.getWarningsForRef(ref, warning !== undefined);
if (!warning) { if (!warning) {
if (w) { if (w) delete w[ref.key];
delete w[ref.key];
}
} else { } else {
warning.level = warning.level || 'warning'; warning.level = warning.level || 'warning';
w[ref.key] = { ref: ref, warning: warning }; w[ref.key] = { ref: ref, warning: warning };
@@ -82,34 +47,31 @@ export class WarningsModel extends Model {
this.scheduleNotifyChanged(); this.scheduleNotifyChanged();
} }
public clearWarningsForRef(ref: WarningRef) { public clearWarningsForRef(ref) {
const w = this.getWarningsForRef(ref); var w = this.getWarningsForRef(ref);
if (!w) return; if (!w) return;
for (const i in w) { for (var i in w) delete w[i];
delete w[i];
}
this.scheduleNotifyChanged(); this.scheduleNotifyChanged();
} }
public clearAllWarningsForComponent(component: ComponentModel) { public clearAllWarningsForComponent(component) {
const warnings = this.warnings[component.name]; const warnings = this.warnings[component.name];
if (!warnings) return; if (!warnings) return;
for (const i in warnings) { for (let i in warnings) delete warnings[i];
delete warnings[i];
}
this.scheduleNotifyChanged(); this.scheduleNotifyChanged();
} }
public clearWarningsForRefMatching(matchFn: (ref: TSFixme) => boolean) { public clearWarningsForRefMatching(matchCb) {
for (const cw of Object.values(this.warnings)) { for (const cw of Object.values(this.warnings)) {
for (const ws of Object.values(cw)) { for (const ws of Object.values(cw)) {
for (const key in ws) { for (const key in ws) {
const w = ws[key]; const w = ws[key];
if (matchFn(w.ref)) { if (matchCb(w.ref)) {
delete ws[key]; delete ws[key];
} }
} }
@@ -125,14 +87,15 @@ export class WarningsModel extends Model {
this.scheduleNotifyChanged(); this.scheduleNotifyChanged();
} }
public getWarnings(ref: WarningRef) { public getWarnings(ref) {
const w = this.getWarningsForRef(ref); var w = this.getWarningsForRef(ref);
if (!w) return; if (!w) return;
if (Object.keys(w).length === 0) return; if (Object.keys(w).length === 0) return;
// Create short message for hover // Create short message for hover
const messages = []; var messages = [];
for (const k in w) { for (var k in w) {
if (w[k].warning) messages.push(w[k].warning.message); if (w[k].warning) messages.push(w[k].warning.message);
} }
@@ -143,13 +106,13 @@ export class WarningsModel extends Model {
} }
public forEachWarningInComponent(c, callback, args) { public forEachWarningInComponent(c, callback, args) {
const cw = this.warnings[c ? c.name : '/']; var cw = this.warnings[c ? c.name : '/'];
if (!cw) return; if (!cw) return;
for (const ref in cw) { for (var ref in cw) {
const ws = cw[ref]; var ws = cw[ref];
for (const w in ws) { for (var w in ws) {
if (!args || !args.levels || args.levels.indexOf(ws[w].warning.level) !== -1) { if (!args || !args.levels || args.levels.indexOf(ws[w].warning.level) !== -1) {
callback(ws[w]); callback(ws[w]);
} }
@@ -158,7 +121,7 @@ export class WarningsModel extends Model {
} }
public getAllWarningsForComponent(c, args) { public getAllWarningsForComponent(c, args) {
const warnings = []; var warnings = [];
this.forEachWarningInComponent( this.forEachWarningInComponent(
c, c,
function (warning) { function (warning) {
@@ -189,15 +152,15 @@ export class WarningsModel extends Model {
} }
public getTotalNumberOfWarnings(args) { public getTotalNumberOfWarnings(args) {
let total = 0; var total = 0;
for (const key in this.warnings) { for (var key in this.warnings) {
total += this.getNumberOfWarningsForComponent({ name: key }, args); total += this.getNumberOfWarningsForComponent({ name: key }, args);
} }
return total; return total;
} }
public getTotalNumberOfWarningsMatching(matchCb) { public getTotalNumberOfWarningsMatching(matchCb) {
let total = 0; var total = 0;
this.forEachWarning((c, ref, key, warning) => { this.forEachWarning((c, ref, key, warning) => {
if (matchCb(key, ref, warning)) total++; if (matchCb(key, ref, warning)) total++;
}); });
@@ -205,16 +168,16 @@ export class WarningsModel extends Model {
} }
public forEachWarning(callback: (c: string, ref, key, warning) => void) { public forEachWarning(callback: (c: string, ref, key, warning) => void) {
const w = this.warnings; var w = this.warnings;
for (const c in w) { for (const c in w) {
// Loop over all components // Loop over all components
const _w = w[c]; var _w = w[c];
for (const ref in _w) { for (const ref in _w) {
// Loop over all refs // Loop over all refs
const __w = _w[ref]; var __w = _w[ref];
for (const key in __w) { for (const key in __w) {
// Loop over all keys // Loop over all keys
const warning = __w[key]; var warning = __w[key];
callback(c, ref, key, warning); callback(c, ref, key, warning);
} }
@@ -232,12 +195,12 @@ export class WarningsModel extends Model {
// node: nodeRef, // node: nodeRef,
// connection: connectionRef, // connection: connectionRef,
// key: key of warning as string} // key: key of warning as string}
private getWarningsForRef(ref: WarningRef, create?) { private getWarningsForRef(ref, create?) {
const componentName = ref.component ? ref.component.name : '/'; var componentName = ref.component ? ref.component.name : '/';
if (!this.warnings[componentName]) this.warnings[componentName] = {}; if (!this.warnings[componentName]) this.warnings[componentName] = {};
const cw = this.warnings[componentName]; var cw = this.warnings[componentName];
let key; var key;
if (ref.node) key = 'node/' + ref.node.id; if (ref.node) key = 'node/' + ref.node.id;
else if (ref.connection) else if (ref.connection)
key = key =
@@ -257,8 +220,7 @@ export class WarningsModel extends Model {
/** Batch changed notifications so listeners don't get peppered */ /** Batch changed notifications so listeners don't get peppered */
private scheduleNotifyChanged() { private scheduleNotifyChanged() {
// eslint-disable-next-line @typescript-eslint/no-this-alias var _this = this;
const _this = this;
if (this.notifyChangedScheduled) return; if (this.notifyChangedScheduled) return;
this.notifyChangedScheduled = true; this.notifyChangedScheduled = true;

View File

@@ -4,7 +4,7 @@ import { clearFolders } from './cleanup';
export async function copyProjectFilesToFolder(projectPath: string, direntry: string): Promise<void> { export async function copyProjectFilesToFolder(projectPath: string, direntry: string): Promise<void> {
// TODO: Load something like .noodlignore file list // TODO: Load something like .noodlignore file list
const ignoreFiles = ['.DS_Store', '.gitignore', '.gitattributes', 'project.json', 'Dockerfile', 'nodelibrary.json']; const ignoreFiles = ['.DS_Store', '.gitignore', '.gitattributes', 'project.json', 'Dockerfile'];
// Copy everything from the project folder // Copy everything from the project folder
if (!projectPath) { if (!projectPath) {

View File

@@ -1,32 +1,22 @@
import type { ComponentModel } from '@noodl-models/componentmodel'; const { ProjectModel } = require('../models/projectmodel');
import type { NodeGraphNode } from '@noodl-models/nodegraphmodel';
import { ProjectModel } from '../models/projectmodel'; function matchStrings(string1, string2) {
export type SearchResult = {
componentTarget: ComponentModel;
nodeTarget?: NodeGraphNode;
type?: string;
userLabel?: string;
label: string;
};
function matchStrings(string1: string, string2: string) {
return string1.toLowerCase().indexOf(string2.toLowerCase()) !== -1; return string1.toLowerCase().indexOf(string2.toLowerCase()) !== -1;
} }
function searchInNodeRecursive(node: NodeGraphNode, searchTerms: string, component: ComponentModel) { function searchInNodeRecursive(node, searchTerms, component) {
let results: SearchResult[] = []; var results = [];
let matchLabel: string | null = null; var matchLabel = null;
var i = 0;
if (node._label !== undefined && matchStrings(node._label, searchTerms)) { if (node._label !== undefined && matchStrings(node._label, searchTerms)) {
matchLabel = node.label; matchLabel = node.label;
} else if (node.id === searchTerms) { } else if (matchStrings(node.id, searchTerms)) {
matchLabel = node.id; matchLabel = node.id;
} else if (matchStrings(node.type.displayName || node.type.name, searchTerms)) { } else if (matchStrings(node.type.displayName || node.type.name, searchTerms)) {
matchLabel = node.label; matchLabel = node.label;
} else { } else {
const parameterNames = Object.keys(node.parameters); let parameterNames = Object.keys(node.parameters);
for (const parameterNameIndex in parameterNames) { for (const parameterNameIndex in parameterNames) {
const parameterName = parameterNames[parameterNameIndex]; const parameterName = parameterNames[parameterNameIndex];
@@ -35,7 +25,7 @@ function searchInNodeRecursive(node: NodeGraphNode, searchTerms: string, compone
matchStrings(node.parameters[parameterName], searchTerms) matchStrings(node.parameters[parameterName], searchTerms)
) { ) {
let displayLabel = parameterName; let displayLabel = parameterName;
const connectionPort = node.type.ports?.find((port) => port.name === parameterName); let connectionPort = node.type.ports?.find((port) => port.name === parameterName);
if (connectionPort) { if (connectionPort) {
displayLabel = connectionPort.displayName; displayLabel = connectionPort.displayName;
} }
@@ -61,9 +51,9 @@ function searchInNodeRecursive(node: NodeGraphNode, searchTerms: string, compone
} }
if (matchLabel === null) { if (matchLabel === null) {
const ports = node.dynamicports; var ports = node.dynamicports;
for (let i = 0; i < ports.length; ++i) { for (i = 0; i < ports.length; ++i) {
const port = ports[i]; var port = ports[i];
if (matchStrings(port.name, searchTerms)) { if (matchStrings(port.name, searchTerms)) {
matchLabel = node.label + ' : ' + port.name; matchLabel = node.label + ' : ' + port.name;
break; break;
@@ -72,9 +62,9 @@ function searchInNodeRecursive(node: NodeGraphNode, searchTerms: string, compone
} }
if (matchLabel === null) { if (matchLabel === null) {
const ports = node.ports; var ports = node.ports;
for (let i = 0; i < ports.length; ++i) { for (i = 0; i < ports.length; ++i) {
const port = ports[i]; var port = ports[i];
if (matchStrings(port.name, searchTerms)) { if (matchStrings(port.name, searchTerms)) {
matchLabel = node.label + ' : ' + port.name; matchLabel = node.label + ' : ' + port.name;
break; break;
@@ -93,17 +83,17 @@ function searchInNodeRecursive(node: NodeGraphNode, searchTerms: string, compone
}); });
} }
for (let i = 0; i < node.children.length; ++i) { for (i = 0; i < node.children.length; ++i) {
const child = node.children[i]; var child = node.children[i];
const childResults = searchInNodeRecursive(child, searchTerms, component); var childResults = searchInNodeRecursive(child, searchTerms, component);
results = results.concat(childResults); results = results.concat(childResults);
} }
return results; return results;
} }
function searchInComponent(component: ComponentModel, searchTerms: string) { function searchInComponent(component, searchTerms) {
let results: SearchResult[] = []; var results = [];
if (matchStrings(component.displayName, searchTerms)) { if (matchStrings(component.displayName, searchTerms)) {
results.push({ results.push({
componentTarget: component, componentTarget: component,
@@ -112,14 +102,14 @@ function searchInComponent(component: ComponentModel, searchTerms: string) {
}); });
} }
for (let i = 0; i < component.graph.roots.length; ++i) { for (var i = 0; i < component.graph.roots.length; ++i) {
const node = component.graph.roots[i]; var node = component.graph.roots[i];
const nodeResults = searchInNodeRecursive(node, searchTerms, component); var nodeResults = searchInNodeRecursive(node, searchTerms, component);
results = results.concat(nodeResults); results = results.concat(nodeResults);
} }
if (component.graph.commentsModel.comments) { if (component.graph.commentsModel.comments) {
for (let i = 0; i < component.graph.commentsModel.comments.length; ++i) { for (var i = 0; i < component.graph.commentsModel.comments.length; ++i) {
const comment = component.graph.commentsModel.comments[i]; const comment = component.graph.commentsModel.comments[i];
if (matchStrings(comment.text, searchTerms)) { if (matchStrings(comment.text, searchTerms)) {
results.push({ results.push({
@@ -142,17 +132,17 @@ function searchInComponent(component: ComponentModel, searchTerms: string) {
} }
} }
export function performSearch(searchTerms: string) { export function performSearch(searchTerms) {
const results: ReturnType<typeof searchInComponent>[] = []; var results = [];
const root = ProjectModel.instance.getRootNode(); var root = ProjectModel.instance.getRootNode();
if (root === undefined) return; if (root === undefined) return;
const components = ProjectModel.instance.components; var components = ProjectModel.instance.components;
for (let i = 0; i < components.length; ++i) { for (var i = 0; i < components.length; ++i) {
const component = components[i]; var component = components[i];
const componentResults = searchInComponent(component, searchTerms); var componentResults = searchInComponent(component, searchTerms);
if (componentResults !== null) { if (componentResults !== null) {
//limit the label length (it can search in markdown, css, etc) //limit the label length (it can search in markdown, css, etc)
for (const result of componentResults.results) { for (const result of componentResults.results) {

View File

@@ -211,7 +211,7 @@ export default function Clippy() {
aiAssistantModel.removeActivity(id); aiAssistantModel.removeActivity(id);
} }
const initialPlaceholder = isInputOpen ? 'Select (or type) a command below' : 'Ask FluxScape AI'; const initialPlaceholder = isInputOpen ? 'Select (or type) a command below' : 'Ask Noodl AI';
const isPromptInWrongOrder = Boolean(!selectedOption) && Boolean(secondInputValue); const isPromptInWrongOrder = Boolean(!selectedOption) && Boolean(secondInputValue);
const isFullBeta = ['full-beta', 'enterprise'].includes(version); const isFullBeta = ['full-beta', 'enterprise'].includes(version);
const isLimitedBeta = false; // TODO: version === 'limited-beta'; const isLimitedBeta = false; // TODO: version === 'limited-beta';
@@ -412,8 +412,8 @@ export default function Clippy() {
<Text hasBottomSpacing>4. Click the "Verify API Key" button</Text> <Text hasBottomSpacing>4. Click the "Verify API Key" button</Text>
<Text hasBottomSpacing> <Text hasBottomSpacing>
If you dont have an API key with GPT-4 access, you can set the FluxScape AI to use the Limited Beta in If you dont have an API key with GPT-4 access, you can set the Noodl AI to use the Limited Beta in the
the editor settings. editor settings.
</Text> </Text>
<PrimaryButton <PrimaryButton
size={PrimaryButtonSize.Small} size={PrimaryButtonSize.Small}
@@ -597,8 +597,8 @@ export default function Clippy() {
</Label> </Label>
<Text hasBottomSpacing size={TextSize.Medium}> <Text hasBottomSpacing size={TextSize.Medium}>
You are running the limited beta of FluxScape AI. If features fewer commands and a less capable AI. Get You are running the limited beta of Noodl AI. If features fewer commands and a less capable AI. Get full
full beta access by bringing your own GPT-4 API key. beta access by bringing your own GPT-4 API key.
</Text> </Text>
<PrimaryButton <PrimaryButton

View File

@@ -1,5 +1,4 @@
import React, { RefObject } from 'react'; import React, { RefObject } from 'react';
import { platform } from '@noodl/platform';
import { ProjectModel } from '@noodl-models/projectmodel'; import { ProjectModel } from '@noodl-models/projectmodel';
@@ -65,9 +64,7 @@ export function DeployPopup(props: DeployPopupProps) {
} }
function FluxscapeDeployTab() { function FluxscapeDeployTab() {
const params = { const params = {};
version: platform.getVersion()
};
const projectId = ProjectModel.instance.id; const projectId = ProjectModel.instance.id;
if (projectId) { if (projectId) {

View File

@@ -6,6 +6,8 @@ import { CloudService } from '@noodl-models/CloudServices';
import { ActivityIndicator } from '@noodl-core-ui/components/common/ActivityIndicator'; import { ActivityIndicator } from '@noodl-core-ui/components/common/ActivityIndicator';
import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon'; import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
import { IconButton } from '@noodl-core-ui/components/inputs/IconButton'; import { IconButton } from '@noodl-core-ui/components/inputs/IconButton';
import { PrimaryButton } from '@noodl-core-ui/components/inputs/PrimaryButton';
import { Box } from '@noodl-core-ui/components/layout/Box';
import { ConditionalContainer } from '@noodl-core-ui/components/layout/ConditionalContainer'; import { ConditionalContainer } from '@noodl-core-ui/components/layout/ConditionalContainer';
import { Container } from '@noodl-core-ui/components/layout/Container'; import { Container } from '@noodl-core-ui/components/layout/Container';
import { VStack } from '@noodl-core-ui/components/layout/Stack'; import { VStack } from '@noodl-core-ui/components/layout/Stack';

View File

@@ -44,7 +44,7 @@ export function OpenAiSection() {
} }
return ( return (
<CollapsableSection title="FluxScape AI (Beta)"> <CollapsableSection title="Noodl AI (Beta)">
<Box hasXSpacing> <Box hasXSpacing>
<VStack> <VStack>
<PropertyPanelRow label="Version" isChanged={false}> <PropertyPanelRow label="Version" isChanged={false}>
@@ -66,7 +66,7 @@ export function OpenAiSection() {
{enabledState === 'disabled' && ( {enabledState === 'disabled' && (
<Box hasYSpacing> <Box hasYSpacing>
<Text>FluxScape AI is currently disabled.</Text> <Text>Noodl AI is currently disabled.</Text>
</Box> </Box>
)} )}
@@ -157,16 +157,16 @@ export function OpenAiSection() {
UNSAFE_style={{ borderRadius: '2px', background: 'var(--theme-color-bg-3)' }} UNSAFE_style={{ borderRadius: '2px', background: 'var(--theme-color-bg-3)' }}
> >
<Title size={TitleSize.Medium} hasBottomSpacing> <Title size={TitleSize.Medium} hasBottomSpacing>
FluxScape AI docs Noodl AI docs
</Title> </Title>
<Text hasBottomSpacing>See setup instructions and guides for how to use FluxScape AI on our docs.</Text> <Text hasBottomSpacing>See setup instructions and guides for how to use Noodl AI on our docs.</Text>
<PrimaryButton <PrimaryButton
variant={PrimaryButtonVariant.Muted} variant={PrimaryButtonVariant.Muted}
size={PrimaryButtonSize.Small} size={PrimaryButtonSize.Small}
isGrowing isGrowing
label="Open docs" label="Open docs"
onClick={() => { onClick={() => {
platform.openExternal('https://docs.fluxscape.io/docs/getting-started/noodl-ai'); platform.openExternal('https://docs.noodl.net/#/docs/getting-started/noodl-ai/');
}} }}
/> />
</Box> </Box>

View File

@@ -991,7 +991,7 @@ export class ComponentsPanelView extends View {
// Find references // Find references
const nodeReference = HACK_findNodeReference(scope.comp.name); const nodeReference = HACK_findNodeReference(scope.comp.name);
const nodeReferencesText = `Used in ~${nodeReference?.referenaces?.length || 0} places`; const nodeReferencesText = `Used in ${nodeReference?.referenaces?.length || 0} places`;
items = items.concat([ items = items.concat([
{ {
@@ -1122,7 +1122,7 @@ export class ComponentsPanelView extends View {
if (scope.canBecomeRoot) { if (scope.canBecomeRoot) {
// Find references // Find references
const nodeReference = HACK_findNodeReference(scope.folder.component.name); const nodeReference = HACK_findNodeReference(scope.folder.component.name);
const nodeReferencesText = `Used in ~${nodeReference?.referenaces?.length || 0} places`; const nodeReferencesText = `Used in ${nodeReference?.referenaces?.length || 0} places`;
items = items.concat([{ items = items.concat([{
label: nodeReferencesText label: nodeReferencesText

View File

@@ -182,7 +182,7 @@ function AiNodeChat({ context, onUpdated }: AiNodeChatProps) {
footer={ footer={
version === 'disabled' ? ( version === 'disabled' ? (
<Center> <Center>
<Text textType={TextType.Shy}>FluxScape AI is currently disabled.</Text> <Text textType={TextType.Shy}>Noodl AI is currently disabled.</Text>
</Center> </Center>
) : ( ) : (
<> <>

View File

@@ -5,8 +5,10 @@ import { useSidePanelKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
import classNames from 'classnames'; import classNames from 'classnames';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { ComponentModel } from '@noodl-models/componentmodel';
import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode'; import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode';
import { performSearch, SearchResult } from '@noodl-utils/universal-search'; import { performSearch } from '@noodl-utils/universal-search';
import { SearchInput } from '@noodl-core-ui/components/inputs/SearchInput'; import { SearchInput } from '@noodl-core-ui/components/inputs/SearchInput';
import { Container } from '@noodl-core-ui/components/layout/Container'; import { Container } from '@noodl-core-ui/components/layout/Container';
@@ -19,7 +21,7 @@ import css from './search-panel.module.scss';
export function SearchPanel() { export function SearchPanel() {
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [searchResults, setSearchResults] = useState<ReturnType<typeof performSearch>>([]); const [searchResults, setSearchResults] = useState([]);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
// TODO: Not same context // TODO: Not same context
@@ -54,7 +56,7 @@ export function SearchPanel() {
} }
}, [debouncedSearchTerm]); }, [debouncedSearchTerm]);
function onSearchItemClicked(searchResult: SearchResult) { function onSearchItemClicked(searchResult: SearchResultItem) {
if (searchResult.type === 'Component') { if (searchResult.type === 'Component') {
NodeGraphContextTmp.switchToComponent(searchResult.componentTarget, { NodeGraphContextTmp.switchToComponent(searchResult.componentTarget, {
breadcrumbs: false, breadcrumbs: false,
@@ -97,9 +99,21 @@ export function SearchPanel() {
); );
} }
type SearchResultItem = {
componentTarget: ComponentModel;
label: string;
nodeTarget: NodeGraphNode;
type: string;
userLabel: string;
};
type SearchItemProps = { type SearchItemProps = {
component: ReturnType<typeof performSearch>[0]; component: {
onSearchItemClicked: (item: SearchResult) => void; componentName: string;
componentId: string;
results: SearchResultItem[];
};
onSearchItemClicked: (item: SearchResultItem) => void;
}; };
function SearchItem({ component, onSearchItemClicked }: SearchItemProps) { function SearchItem({ component, onSearchItemClicked }: SearchItemProps) {
@@ -116,11 +130,11 @@ function SearchItem({ component, onSearchItemClicked }: SearchItemProps) {
<Section title={titleText} variant={SectionVariant.Panel}> <Section title={titleText} variant={SectionVariant.Panel}>
{component.results.map((result, index) => ( {component.results.map((result, index) => (
<div <div
key={index}
className={classNames( className={classNames(
css.SearchResultItem css.SearchResultItem
// lastActiveComponentId === result.componentTarget.id && css['is-active'] // lastActiveComponentId === result.componentTarget.id && css['is-active']
)} )}
key={index}
onClick={() => onSearchItemClicked(result)} onClick={() => onSearchItemClicked(result)}
> >
<Label variant={TextType.Proud}> <Label variant={TextType.Proud}>

View File

@@ -263,9 +263,7 @@ class CloudStore {
* @param {{ * @param {{
* objectId: string; * objectId: string;
* collection: string; * collection: string;
* keys?: string[] | string;
* include?: string[] | string; * include?: string[] | string;
* excludeKeys?: string[] | string;
* success: (data: unknown) => void; * success: (data: unknown) => void;
* error: (error: unknown) => void; * error: (error: unknown) => void;
* }} options * }} options
@@ -277,16 +275,6 @@ class CloudStore {
args.push('include=' + (Array.isArray(options.include) ? options.include.join(',') : options.include)); args.push('include=' + (Array.isArray(options.include) ? options.include.join(',') : options.include));
} }
if (options.keys) {
args.push('keys=' + (Array.isArray(options.keys) ? options.keys.join(',') : options.keys));
}
if (options.excludeKeys) {
args.push(
'excludeKeys=' + (Array.isArray(options.excludeKeys) ? options.excludeKeys.join(',') : options.excludeKeys)
);
}
this._makeRequest( this._makeRequest(
'/classes/' + options.collection + '/' + options.objectId + (args.length > 0 ? '?' + args.join('&') : ''), '/classes/' + options.collection + '/' + options.objectId + (args.length > 0 ? '?' + args.join('&') : ''),
{ {
@@ -471,15 +459,8 @@ class CloudStore {
} }
function _isArrayOfObjects(a) { function _isArrayOfObjects(a) {
if (!Array.isArray(a)) { if (!Array.isArray(a)) return false;
return false; for (var i = 0; i < a.length; i++) if (typeof a[i] !== 'object' || a[i] === null) return false;
}
for (let i = 0; i < a.length; i++) {
if (typeof a[i] !== 'object' || a[i] === null) {
return false;
}
}
return true; return true;
} }
@@ -551,86 +532,54 @@ function _serializeObject(data, collectionName, modelScope) {
return data; return data;
} }
/**
*
* @param {unknown} data
* @param {string} type
* @param {*} modelScope
* @returns
*/
function _deserializeJSON(data, type, modelScope) { function _deserializeJSON(data, type, modelScope) {
if (data === undefined) return undefined; if (data === undefined) return;
if (data === null) return null; if (data === null) return null;
if (type === 'Relation' && data.__type === 'Relation') { if (type === 'Relation' && data.__type === 'Relation') {
return undefined; // Ignore relation fields return undefined; // Ignore relation fields
} } else if (type === 'Pointer' && data.__type === 'Pointer') {
// This is a pointer type, resolve into id
// This is a pointer type, resolve into id
if (type === 'Pointer' && data.__type === 'Pointer') {
return data.objectId; return data.objectId;
} } else if (type === 'Date' && data.__type === 'Date') {
if (type === 'Date' && data.__type === 'Date') {
return new Date(data.iso); return new Date(data.iso);
} } else if (type === 'Date' && typeof data === 'string') {
if (type === 'Date' && typeof data === 'string') {
return new Date(data); return new Date(data);
} } else if (type === 'File' && data.__type === 'File') {
if (type === 'File' && data.__type === 'File') {
return new CloudFile(data); return new CloudFile(data);
} } else if (type === 'GeoPoint' && data.__type === 'GeoPoint') {
if (type === 'GeoPoint' && data.__type === 'GeoPoint') {
return { return {
latitude: data.latitude, latitude: data.latitude,
longitude: data.longitude longitude: data.longitude
}; };
} } else if (_isArrayOfObjects(data)) {
var a = [];
if (_isArrayOfObjects(data)) { for (var i = 0; i < data.length; i++) {
const a = [];
for (let i = 0; i < data.length; i++) {
a.push(_deserializeJSON(data[i], undefined, modelScope)); a.push(_deserializeJSON(data[i], undefined, modelScope));
} }
const c = Collection.get(); var c = Collection.get();
c.set(a); c.set(a);
return c; return c;
} } else if (Array.isArray(data)) return data;
// An array with mixed types
if (Array.isArray(data)) {
return data;
}
// This is an array with mixed data, just return it // This is an array with mixed data, just return it
if (data && data.__type === 'Object' && data.className !== undefined && data.objectId !== undefined) { else if (data && data.__type === 'Object' && data.className !== undefined && data.objectId !== undefined) {
const _data = Object.assign({}, data); const _data = Object.assign({}, data);
delete _data.className; delete _data.className;
delete _data.__type; delete _data.__type;
return _fromJSON(_data, data.className, modelScope); return _fromJSON(_data, data.className, modelScope);
} } else if (typeof data === 'object' && data !== null) {
var m = (modelScope || Model).get();
if (typeof data === 'object' && data !== null) { for (var key in data) {
// Try to get the model by id, if it is defined, otherwise we create a new unique id. m.set(key, _deserializeJSON(data[key], undefined, modelScope));
const model = (modelScope || Model).get(data.id);
for (const key in data) {
const nestedValue = _deserializeJSON(data[key], undefined, modelScope);
model.set(key, nestedValue);
} }
return model; return m;
} } else return data;
return data;
} }
function _fromJSON(item, collectionName, modelScope) { function _fromJSON(item, collectionName, modelScope) {
const modelStore = modelScope || Model; const modelStore = modelScope || Model;
// Try to get the model by the object id (record) or id, otherwise we create a new unique id. const model = modelStore.get(item.objectId);
const model = modelStore.get(item.objectId || item.id);
model._class = collectionName; model._class = collectionName;
let schema = undefined; let schema = undefined;
@@ -644,8 +593,7 @@ function _fromJSON(item, collectionName, modelScope) {
} }
const _type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined; const _type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
const nestedValue = _deserializeJSON(item[key], _type, modelScope); model.set(key, _deserializeJSON(item[key], _type, modelScope));
model.set(key, nestedValue);
} }
return model; return model;

View File

@@ -112,9 +112,7 @@ function createRecordsAPI(modelScope) {
* @param {string | { getId(): string; }} objectOrId * @param {string | { getId(): string; }} objectOrId
* @param {{ * @param {{
* className: string; * className: string;
* keys?: string[] | string;
* include?: string[] | string; * include?: string[] | string;
* excludeKeys?: string[] | string;
* }} options * }} options
* @returns {Promise<unknown>} * @returns {Promise<unknown>}
*/ */
@@ -131,9 +129,7 @@ function createRecordsAPI(modelScope) {
cloudstore().fetch({ cloudstore().fetch({
collection: className, collection: className,
objectId: objectOrId, objectId: objectOrId,
keys: options?.keys, include: options ? options.include : undefined,
include: options?.include,
excludeKeys: options?.excludeKeys,
success: function (response) { success: function (response) {
const record = cloudstore()._fromJSON(response, className); const record = cloudstore()._fromJSON(response, className);
resolve(record); resolve(record);

View File

@@ -1,6 +1,6 @@
'use strict'; "use strict";
var Model = require('./model'); var Model = require("./model");
// Get and set proxy // Get and set proxy
/*const proxies = {} /*const proxies = {}
@@ -221,48 +221,48 @@ Collection.prototype.toJSON = function() {
}*/ }*/
// ---- // ----
Object.defineProperty(Array.prototype, 'items', { Object.defineProperty(Array.prototype, "items", {
enumerable: false, enumerable: false,
get() { get() {
return this; return this;
}, },
set(data) { set(data) {
this.set(data); this.set(data);
} },
}); });
Object.defineProperty(Array.prototype, 'each', { Object.defineProperty(Array.prototype, "each", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: Array.prototype.forEach value: Array.prototype.forEach,
}); });
Object.defineProperty(Array.prototype, 'size', { Object.defineProperty(Array.prototype, "size", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function () { value: function () {
return this.length; return this.length;
} },
}); });
Object.defineProperty(Array.prototype, 'get', { Object.defineProperty(Array.prototype, "get", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (index) { value: function (index) {
return this[index]; return this[index];
} },
}); });
Object.defineProperty(Array.prototype, 'getId', { Object.defineProperty(Array.prototype, "getId", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function () { value: function () {
return this._id; return this._id;
} },
}); });
Object.defineProperty(Array.prototype, 'id', { Object.defineProperty(Array.prototype, "id", {
enumerable: false, enumerable: false,
get() { get() {
return this.getId(); return this.getId();
} },
}); });
Object.defineProperty(Array.prototype, 'set', { Object.defineProperty(Array.prototype, "set", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (src) { value: function (src) {
@@ -323,10 +323,10 @@ Object.defineProperty(Array.prototype, 'set', {
for (i = aItems.length; i < bItems.length; i++) { for (i = aItems.length; i < bItems.length; i++) {
this.add(bItems[i]); this.add(bItems[i]);
} }
} },
}); });
Object.defineProperty(Array.prototype, 'notify', { Object.defineProperty(Array.prototype, "notify", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: async function (event, args) { value: async function (event, args) {
@@ -337,80 +337,80 @@ Object.defineProperty(Array.prototype, 'notify', {
for (var i = 0; i < l.length; i++) { for (var i = 0; i < l.length; i++) {
await l[i](args); await l[i](args);
} }
} },
}); });
Object.defineProperty(Array.prototype, 'contains', { Object.defineProperty(Array.prototype, "contains", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (item) { value: function (item) {
return this.indexOf(item) !== -1; return this.indexOf(item) !== -1;
} },
}); });
Object.defineProperty(Array.prototype, 'add', { Object.defineProperty(Array.prototype, "add", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: async function (item) { value: async function (item) {
if (this.contains(item)) return; // Already contains item if (this.contains(item)) return; // Already contains item
this.items.push(item); this.items.push(item);
await this.notify('add', { item: item, index: this.items.length - 1 }); await this.notify("add", { item: item, index: this.items.length - 1 });
await this.notify('change'); await this.notify("change");
await item.notify('add', { collection: this }); await item.notify("add", { collection: this });
} },
}); });
Object.defineProperty(Array.prototype, 'remove', { Object.defineProperty(Array.prototype, "remove", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (item) { value: function (item) {
var idx = this.items.indexOf(item); var idx = this.items.indexOf(item);
if (idx !== -1) this.removeAtIndex(idx); if (idx !== -1) this.removeAtIndex(idx);
} },
}); });
Object.defineProperty(Array.prototype, 'addAtIndex', { Object.defineProperty(Array.prototype, "addAtIndex", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: async function (item, index) { value: async function (item, index) {
if (this.contains(item)) return; // Already contains item if (this.contains(item)) return; // Already contains item
this.items.splice(index, 0, item); this.items.splice(index, 0, item);
await this.notify('add', { item: item, index: index }); await this.notify("add", { item: item, index: index });
await this.notify('change'); await this.notify("change");
await item.notify('add', { collection: this, index: index }); await item.notify("add", { collection: this, index: index });
} },
}); });
Object.defineProperty(Array.prototype, 'removeAtIndex', { Object.defineProperty(Array.prototype, "removeAtIndex", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: async function (idx) { value: async function (idx) {
var item = this.items[idx]; var item = this.items[idx];
this.items.splice(idx, 1); this.items.splice(idx, 1);
await this.notify('remove', { item: item, index: idx }); await this.notify("remove", { item: item, index: idx });
await this.notify('change'); await this.notify("change");
await item.notify('remove', { collection: this }); await item.notify("remove", { collection: this });
} },
}); });
Object.defineProperty(Array.prototype, 'on', { Object.defineProperty(Array.prototype, "on", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (event, listener) { value: function (event, listener) {
if (!this._listeners) if (!this._listeners)
Object.defineProperty(this, '_listeners', { Object.defineProperty(this, "_listeners", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: {} value: {},
}); });
if (!this._listeners[event]) this._listeners[event] = []; if (!this._listeners[event]) this._listeners[event] = [];
this._listeners[event].push(listener); this._listeners[event].push(listener);
} },
}); });
Object.defineProperty(Array.prototype, 'off', { Object.defineProperty(Array.prototype, "off", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: function (event, listener) { value: function (event, listener) {
@@ -418,20 +418,20 @@ Object.defineProperty(Array.prototype, 'off', {
if (!this._listeners[event]) return; if (!this._listeners[event]) return;
var idx = this._listeners[event].indexOf(listener); var idx = this._listeners[event].indexOf(listener);
if (idx !== -1) this._listeners[event].splice(idx, 1); if (idx !== -1) this._listeners[event].splice(idx, 1);
} },
}); });
class Collection extends Array {} class Collection extends Array {}
const collections = (Collection._collections = {}); var collections = (Collection._collections = {});
Collection.create = function (items) { Collection.create = function (items) {
const name = Model.guid(); const name = Model.guid();
collections[name] = new Collection(); collections[name] = new Collection();
Object.defineProperty(collections[name], '_id', { Object.defineProperty(collections[name], "_id", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: name value: name,
}); });
if (items) { if (items) {
collections[name].set(items); collections[name].set(items);
@@ -439,18 +439,14 @@ Collection.create = function (items) {
return collections[name]; return collections[name];
}; };
/**
* @param {string} name
* @returns {Collection}
*/
Collection.get = function (name) { Collection.get = function (name) {
if (name === undefined) name = Model.guid(); if (name === undefined) name = Model.guid();
if (!collections[name]) { if (!collections[name]) {
collections[name] = new Collection(); collections[name] = new Collection();
Object.defineProperty(collections[name], '_id', { Object.defineProperty(collections[name], "_id", {
enumerable: false, enumerable: false,
writable: false, writable: false,
value: name value: name,
}); });
} }

View File

@@ -35,11 +35,6 @@ const _modelProxyHandler = {
} }
}; };
/**
*
* @param {*} id
* @returns {Model}
*/
Model.get = function (id) { Model.get = function (id) {
if (id === undefined) id = Model.guid(); if (id === undefined) id = Model.guid();
if (!models[id]) { if (!models[id]) {
@@ -127,22 +122,13 @@ Model.prototype.fill = function (value = null) {
} }
}; };
/**
* @param {string} name
* @param {unknown} value
* @param {{
* resolve?: boolean;
* forceChange?: boolean;
* silent?: boolean;
* }} args
*/
Model.prototype.set = function (name, value, args) { Model.prototype.set = function (name, value, args) {
if (args && args.resolve && name.indexOf('.') !== -1) { if (args && args.resolve && name.indexOf('.') !== -1) {
// We should resolve path references // We should resolve path references
const path = name.split('.'); var path = name.split('.');
let model = this; var model = this;
for (let i = 0; i < path.length - 1; i++) { for (var i = 0; i < path.length - 1; i++) {
const v = model.get(path[i]); var v = model.get(path[i]);
if (Model.instanceOf(v)) model = v; if (Model.instanceOf(v)) model = v;
else return; // Path resolve failed else return; // Path resolve failed
} }
@@ -152,35 +138,24 @@ Model.prototype.set = function (name, value, args) {
const forceChange = args && args.forceChange; const forceChange = args && args.forceChange;
const oldValue = this.data[name]; var oldValue = this.data[name];
this.data[name] = value; this.data[name] = value;
(forceChange || oldValue !== value) &&
if ((forceChange || oldValue !== value) && (!args || !args.silent)) { (!args || !args.silent) &&
this.notify('change', { name: name, value: value, old: oldValue }); this.notify('change', { name: name, value: value, old: oldValue });
}
}; };
/**
* @returns {string}
*/
Model.prototype.getId = function () { Model.prototype.getId = function () {
return this.id; return this.id;
}; };
/**
* @param {string} name
* @param {{
* resolve?: boolean;
* }} args
* @returns {unknown}
*/
Model.prototype.get = function (name, args) { Model.prototype.get = function (name, args) {
if (args && args.resolve && name.indexOf('.') !== -1) { if (args && args.resolve && name.indexOf('.') !== -1) {
// We should resolve path references // We should resolve path references
const path = name.split('.'); var path = name.split('.');
let model = this; var model = this;
for (let i = 0; i < path.length - 1; i++) { for (var i = 0; i < path.length - 1; i++) {
const v = model.get(path[i]); var v = model.get(path[i]);
if (Model.instanceOf(v)) model = v; if (Model.instanceOf(v)) model = v;
else return; // Path resolve failed else return; // Path resolve failed
} }

View File

@@ -230,7 +230,7 @@ NodeContext.prototype.deregisterComponentModel = function (componentModel) {
NodeContext.prototype.fetchComponentBundle = async function (name) { NodeContext.prototype.fetchComponentBundle = async function (name) {
const fetchBundle = async (name) => { const fetchBundle = async (name) => {
let baseUrl = Noodl.Env['BaseUrl'] || '/'; let baseUrl = Noodl.Env["BaseUrl"] || '/';
let bundleUrl = `${baseUrl}noodl_bundles/${name}.json`; let bundleUrl = `${baseUrl}noodl_bundles/${name}.json`;
const response = await fetch(bundleUrl); const response = await fetch(bundleUrl);
@@ -455,15 +455,6 @@ NodeContext.prototype.setPopupCallbacks = function ({ onShow, onClose }) {
this.onClosePopup = onClose; this.onClosePopup = onClose;
}; };
/**
* @param {string} popupComponent
* @param {Record<string, unknown>} params
* @param {{
* senderNode?: unknown;
* onClosePopup?: (action?: string, results: object) => void;
* }} args
* @returns
*/
NodeContext.prototype.showPopup = async function (popupComponent, params, args) { NodeContext.prototype.showPopup = async function (popupComponent, params, args) {
if (!this.onShowPopup) return; if (!this.onShowPopup) return;

View File

@@ -227,9 +227,6 @@ declare namespace Noodl {
objectOrId: string | { getId(): string; }, objectOrId: string | { getId(): string; },
options?: { options?: {
className?: RecordClassName; className?: RecordClassName;
keys?: string[] | string;
include?: string[] | string;
excludeKeys?: string[] | string;
} }
): Promise<any>; ): Promise<any>;

View File

@@ -2,18 +2,17 @@ const { RouterHandler } = require('../nodes/navigation/router-handler');
const NoodlRuntime = require('@noodl/runtime'); const NoodlRuntime = require('@noodl/runtime');
const navigation = { const navigation = {
/**
* This is set by "packages/noodl-viewer-react/src/noodl-js-api.js"
* @type {NoodlRuntime}
*/
_noodlRuntime: undefined,
async showPopup(componentPath, params) { async showPopup(componentPath, params) {
return new Promise((resolve) => { return new Promise((resolve) => {
navigation._noodlRuntime.context.showPopup(componentPath, params, { navigation._noodlRuntime.context.showPopup(componentPath, params, {
senderNode: this.nodeScope.componentOwner,
/**
* @param {string | undefined} action
* @param {*} results
*/
onClosePopup: (action, results) => { onClosePopup: (action, results) => {
resolve({ resolve({
action: action?.replace('closeAction-', ''), action: (action || '').replace('closeAction-', ''),
parameters: results parameters: results
}); });
} }

View File

@@ -40,8 +40,8 @@ const ClosePopupNode = {
this._internal.closeCallback = cb; this._internal.closeCallback = cb;
}, },
scheduleClose: function () { scheduleClose: function () {
const _this = this; var _this = this;
const internal = this._internal; var internal = this._internal;
if (!internal.hasScheduledClose) { if (!internal.hasScheduledClose) {
internal.hasScheduledClose = true; internal.hasScheduledClose = true;
this.scheduleAfterInputsHaveUpdated(function () { this.scheduleAfterInputsHaveUpdated(function () {
@@ -113,8 +113,9 @@ module.exports = {
var closeActions = node.parameters['closeActions']; var closeActions = node.parameters['closeActions'];
if (closeActions) { if (closeActions) {
closeActions = closeActions ? closeActions.split(',') : undefined; closeActions = closeActions ? closeActions.split(',') : undefined;
for (const i in closeActions) { for (var i in closeActions) {
const p = closeActions[i]; var p = closeActions[i];
ports.push({ ports.push({
type: 'signal', type: 'signal',
plug: 'input', plug: 'input',

View File

@@ -3,17 +3,6 @@ const CloudStore = require('@noodl/runtime/src/api/cloudstore');
('use strict'); ('use strict');
/**
* @param {string} path
* @param {{
* endpoint: string;
* method: string;
* appId: string;
* content: any;
* success: (json: object) => void;
* error: (json: object | undefined) => void;
* }} options
*/
function _makeRequest(path, options) { function _makeRequest(path, options) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
@@ -26,9 +15,7 @@ function _makeRequest(path, options) {
if (xhr.status === 200 || xhr.status === 201) { if (xhr.status === 200 || xhr.status === 201) {
options.success(json); options.success(json);
} else { } else options.error(json);
options.error(json);
}
} }
}; };
@@ -204,7 +191,7 @@ var CloudFunctionNode = {
this.sendSignalOnOutput('success'); this.sendSignalOnOutput('success');
}, },
error: (e) => { error: (e) => {
const error = typeof e === 'string' ? e : e?.error || 'Failed running cloud function.'; const error = typeof e === 'string' ? e : e.error || 'Failed running cloud function.';
this._internal.lastCallResult = { this._internal.lastCallResult = {
status: 'failure', status: 'failure',
error error

View File

@@ -1,24 +1,20 @@
'use strict'; 'use strict';
const { Node } = require('@noodl/runtime');
const Model = require('@noodl/runtime/src/model'); const Model = require('@noodl/runtime/src/model');
const Collection = require('@noodl/runtime/src/collection'); const Collection = require('@noodl/runtime/src/collection');
const SetVariableNodeDefinition = { var SetVariableNodeDefinition = {
name: 'Set Variable', name: 'Set Variable',
docs: 'https://docs.noodl.net/nodes/data/variable/set-variable', docs: 'https://docs.noodl.net/nodes/data/variable/set-variable',
category: 'Data', category: 'Data',
usePortAsLabel: 'name', usePortAsLabel: 'name',
color: 'data', color: 'data',
initialize: function () { initialize: function () {
const internal = this._internal; var internal = this._internal;
internal.variablesModel = Model.get('--ndl--global-variables');
},
getInspectInfo() {
if (this._internal.name) {
return this._internal.variablesModel.get(this._internal.name);
}
return '[No value set]'; internal.variablesModel = Model.get('--ndl--global-variables');
}, },
outputs: { outputs: {
done: { done: {
@@ -78,22 +74,17 @@ const SetVariableNodeDefinition = {
if (this.hasScheduledStore) return; if (this.hasScheduledStore) return;
this.hasScheduledStore = true; this.hasScheduledStore = true;
const internal = this._internal; var internal = this._internal;
this.scheduleAfterInputsHaveUpdated(function () { this.scheduleAfterInputsHaveUpdated(function () {
this.hasScheduledStore = false; this.hasScheduledStore = false;
let value = internal.setWith === 'emptyString' ? '' : internal.value; var value = internal.setWith === 'emptyString' ? '' : internal.value;
// Can set arrays with "id" or array
if (internal.setWith === 'object' && typeof value === 'string') value = Model.get(value);
// Can set arrays with "id" or array
if (internal.setWith === 'array' && typeof value === 'string') value = Collection.get(value);
if (internal.setWith === 'object' && typeof value === 'string') value = Model.get(value); // Can set arrays with "id" or array
if (internal.setWith === 'array' && typeof value === 'string') value = Collection.get(value); // Can set arrays with "id" or array
if (internal.setWith === 'boolean') value = !!value; if (internal.setWith === 'boolean') value = !!value;
// use forceChange to always trigger Variable nodes to send the value on //use forceChange to always trigger Variable nodes to send the value on their output, even if it's the same value twice
// their output, even if it's the same value twice
internal.variablesModel.set(internal.name, value, { internal.variablesModel.set(internal.name, value, {
forceChange: true forceChange: true
}); });
@@ -105,11 +96,10 @@ const SetVariableNodeDefinition = {
return; return;
} }
if (name === 'value') { if (name === 'value')
this.registerInput(name, { this.registerInput(name, {
set: this.setValue.bind(this) set: this.setValue.bind(this)
}); });
}
} }
} }
}; };

View File

@@ -274,9 +274,6 @@ declare namespace Noodl {
objectOrId: string | { getId(): string; }, objectOrId: string | { getId(): string; },
options?: { options?: {
className?: RecordClassName; className?: RecordClassName;
keys?: string[] | string;
include?: string[] | string;
excludeKeys?: string[] | string;
} }
): Promise<any>; ): Promise<any>;