feat(frontend): merge main

This commit is contained in:
Faton Ramadani
2023-03-11 00:14:49 +01:00
86 changed files with 462 additions and 250 deletions
+1
View File
@@ -4892,6 +4892,7 @@ dependencies = [
"lazy_static",
"phf",
"regex",
"serde_json",
"unicode-general-category",
"windmill-common",
"windmill-parser",
@@ -16,4 +16,5 @@ unicode-general-category.workspace = true
itertools.workspace = true
anyhow.workspace = true
regex.workspace = true
lazy_static.workspace = true
lazy_static.workspace = true
serde_json.workspace = true
@@ -1,6 +1,8 @@
#![allow(non_snake_case)] // TODO: switch to parse_* function naming
use anyhow::anyhow;
use regex::Regex;
use serde_json::json;
use std::collections::HashMap;
use windmill_parser::{Arg, MainArgSignature, Typ};
@@ -17,19 +19,32 @@ pub fn parse_bash_sig(code: &str) -> windmill_common::error::Result<MainArgSigna
}
}
lazy_static::lazy_static! {
static ref RE: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+):-(.*)\})"$"#).unwrap();
}
fn parse_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let mut hm = HashMap::new();
let re = Regex::new(r#"(?m)^(\w+)="\$(\d+)"$"#).unwrap();
for cap in re.captures_iter(code) {
hm.insert(cap[2].parse::<i32>()?, cap[1].to_string());
let mut hm: HashMap<i32, (String, Option<String>)> = HashMap::new();
for cap in RE.captures_iter(code) {
hm.insert(
cap.get(2)
.or(cap.get(3))
.and_then(|x| x.as_str().parse::<i32>().ok())
.ok_or_else(|| anyhow!("Impossible to parse arg digit"))?,
(
cap[1].to_string(),
cap.get(4).map(|x| x.as_str().to_string()),
),
);
}
let mut args = vec![];
for i in 1..20 {
if hm.contains_key(&i) {
let (name, default) = hm.get(&i).unwrap();
args.push(Arg {
name: hm[&i].clone(),
name: name.clone(),
typ: Typ::Str(None),
default: None,
default: default.clone().map(|x| json!(x)),
otyp: None,
has_default: false,
});
@@ -43,6 +58,8 @@ fn parse_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[test]
@@ -50,8 +67,7 @@ mod tests {
let code = r#"
token="$1"
image="$2"
digest="${3:-latest}"
foo="$4"
digest="${3:-latest with spaces}"
"#;
//println!("{}", serde_json::to_string()?);
@@ -74,6 +90,13 @@ foo="$4"
typ: Typ::Str(None),
default: None,
has_default: false
},
Arg {
otyp: None,
name: "digest".to_string(),
typ: Typ::Str(None),
default: Some(json!("latest with spaces")),
has_default: false
}
]
}
+20
View File
@@ -733,6 +733,9 @@ fn build_args(
path: String,
args: &Map<String, Value>,
) -> Result<Map<String, Value>> {
// disallow var and res access in args coming from the user for security reasons
args.into_iter()
.try_for_each(|x| disallow_var_res_access(x.1))?;
let static_args = policy
.triggerables
.get(&path)
@@ -753,3 +756,20 @@ fn build_args(
}
Ok(args)
}
fn disallow_var_res_access(args: &serde_json::Value) -> Result<()> {
match args {
Value::Object(v) => v.into_iter().try_for_each(|x| disallow_var_res_access(x.1)),
Value::Array(arr) => arr.into_iter().try_for_each(|v| disallow_var_res_access(v)),
Value::String(s) => {
if s.starts_with("$var:") || s.starts_with("$res:") {
Err(Error::BadRequest(format!(
"For security reasons, variable or resource access is not allowed as dynamic argument"
)))
} else {
Ok(())
}
}
_ => Ok(()),
}
}
@@ -234,6 +234,14 @@
name,
requestBody: { username: member_name }
})
await GranularAclService.removeGranularAcls({
workspace: $workspaceStore ?? '',
path: name,
kind: 'group_',
requestBody: {
owner: 'u/' + member_name
}
})
loadGroup()
}}>remove</button
>
@@ -58,6 +58,7 @@
on:refresh={async (e) => {
await loadResources(resourceType)
value = e.detail
valueSelect = { value: e.detail, label: e.detail }
}}
newPageOAuth
bind:this={appConnect}
@@ -87,7 +88,7 @@
}}
items={collection}
class="text-clip grow min-w-0"
placeholder="{resourceType} resource"
placeholder="{resourceType ?? 'any'} resource"
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles}
/>
@@ -3,7 +3,7 @@
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
@@ -26,7 +26,7 @@
export const staticOutputs: string[] = ['loading', 'result']
const { runnableComponents, worldStore, app } = getContext<AppEditorContext>('AppEditorContext')
const { runnableComponents, worldStore, app } = getContext<AppViewerContext>('AppViewerContext')
let labelValue: string
let color: ButtonType.Color
@@ -5,7 +5,7 @@
import { Icon } from 'svelte-awesome'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -24,7 +24,7 @@
export const staticOutputs: string[] = ['loading', 'result']
const { app, runnableComponents, worldStore, stateId } =
getContext<AppEditorContext>('AppEditorContext')
getContext<AppViewerContext>('AppViewerContext')
let labelValue: string = 'Default label'
let color: ButtonType.Color
@@ -5,7 +5,7 @@
import { Icon } from 'svelte-awesome'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
@@ -26,7 +26,7 @@
export const staticOutputs: string[] = ['loading', 'result']
const { app, runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, runnableComponents, worldStore } = getContext<AppViewerContext>('AppViewerContext')
let labelValue: string = 'Default label'
let color: ButtonType.Color
@@ -17,7 +17,7 @@
import InputValue from '../helpers/InputValue.svelte'
import { concatCustomCss } from '../../utils'
import { getContext } from 'svelte'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
export let id: string
export let componentInput: AppInput | undefined
@@ -27,7 +27,7 @@
export let render: boolean
export const staticOutputs: string[] = ['loading', 'result']
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
ChartJS.register(
Title,
@@ -5,7 +5,7 @@
import type { AppInput } from '../../inputType'
import {
IS_APP_PUBLIC_CONTEXT_KEY,
type AppEditorContext,
type AppViewerContext,
type ComponentCustomCSS
} from '../../types'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
@@ -17,7 +17,7 @@
export let render: boolean
const requireHtmlApproval = getContext<boolean | undefined>(IS_APP_PUBLIC_CONTEXT_KEY)
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
let result: any = undefined
export const staticOutputs: string[] = ['result', 'loading']
@@ -1,7 +1,7 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
@@ -12,7 +12,7 @@
export let render: boolean
export const staticOutputs: string[] = ['result', 'loading']
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
let result: string | undefined = undefined
let h: number | undefined = undefined
@@ -1,7 +1,7 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import { AlignWrapper, InputValue } from '../helpers'
import { loadIcon } from '../icon'
@@ -14,7 +14,7 @@
export let customCss: ComponentCustomCSS<'container' | 'icon'> | undefined = undefined
export let render: boolean
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
let icon: string | undefined = undefined
let size: number
@@ -3,7 +3,7 @@
import { twMerge } from 'tailwind-merge'
import type { staticValues } from '../../editor/componentsPanel/componentStaticValues'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
@@ -15,7 +15,7 @@
export let customCss: ComponentCustomCSS<'image'> | undefined = undefined
export let render: boolean
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
const fit: Record<FitOption, string> = {
cover: 'object-cover',
contain: 'object-contain',
@@ -2,7 +2,7 @@
import { getContext, onMount } from 'svelte'
import { concatCustomCss } from '../../utils'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { InputValue } from '../helpers'
import { twMerge } from 'tailwind-merge'
import { Map, View, Feature } from 'ol'
@@ -35,7 +35,7 @@
export let render: boolean
const { app, worldStore, selectedComponent, connectingInput, focusedGrid, mode } =
getContext<AppEditorContext>('AppEditorContext')
getContext<AppViewerContext>('AppViewerContext')
$: outputs = $worldStore?.outputsById[id] as {
mapRegion: Output<{
@@ -4,7 +4,7 @@
import { getDocument, type PDFDocumentProxy, type PDFPageProxy } from 'pdfjs-dist'
import 'pdfjs-dist/build/pdf.worker.entry'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
import { throttle } from '../../../../utils'
@@ -19,7 +19,7 @@
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
export let render: boolean
const { app, mode, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { app, mode, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
let source: string | ArrayBuffer | undefined = undefined
let wrapper: HTMLDivElement | undefined = undefined
@@ -14,7 +14,7 @@
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../inputType'
import InputValue from '../helpers/InputValue.svelte'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import { getContext } from 'svelte'
@@ -26,7 +26,7 @@
export let render: boolean
export const staticOutputs: string[] = ['loading', 'result']
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
ChartJS.register(
Title,
@@ -19,7 +19,7 @@
import type { ChartOptions, ChartData } from 'chart.js'
import { concatCustomCss } from '../../utils'
import { getContext } from 'svelte'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
export let id: string
export let componentInput: AppInput | undefined
@@ -32,7 +32,7 @@
let pannable = false
export const staticOutputs: string[] = ['loading', 'result']
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
ChartJS.register(
Title,
@@ -8,7 +8,7 @@
import InputValue from '../helpers/InputValue.svelte'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import { twMerge } from 'tailwind-merge'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { getContext } from 'svelte'
import ResizeWrapper from '../helpers/ResizeWrapper.svelte'
@@ -23,7 +23,7 @@
export const staticOutputs: string[] = ['result', 'loading']
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
let result: string | undefined = undefined
let style: 'Title' | 'Subtitle' | 'Body' | 'Caption' | 'Label' | undefined = undefined
@@ -20,7 +20,7 @@
import { Scatter } from 'svelte-chartjs'
import InputValue from '../helpers/InputValue.svelte'
import type { ChartOptions, ChartData } from 'chart.js'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { getContext } from 'svelte'
import { concatCustomCss } from '../../utils'
@@ -32,7 +32,7 @@
export let render: boolean
export const staticOutputs: string[] = ['loading', 'result']
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
let logarithmicScale = false
let zoomable = false
@@ -5,7 +5,7 @@
import { getContext, onMount } from 'svelte'
import type { Output } from '../../../rx'
import type { AppEditorContext } from '../../../types'
import type { AppViewerContext } from '../../../types'
import InputValue from '../../helpers/InputValue.svelte'
import type { AppInput } from '../../../inputType'
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
@@ -23,7 +23,7 @@
let result: Record<string, any>[] | undefined = undefined
const { worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { worldStore, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
let selectedRowIndex = -1
@@ -1,7 +1,7 @@
<script lang="ts">
import { getContext, onMount } from 'svelte'
import type { Output } from '../../../rx'
import type { AppEditorContext, BaseAppComponent, ComponentCustomCSS } from '../../../types'
import type { AppViewerContext, BaseAppComponent, ComponentCustomCSS } from '../../../types'
import InputValue from '../../helpers/InputValue.svelte'
import type { AppInput } from '../../../inputType'
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
@@ -61,7 +61,7 @@
app,
worldStore,
staticOutputs: staticOutputsStore
} = getContext<AppEditorContext>('AppEditorContext')
} = getContext<AppViewerContext>('AppViewerContext')
let selectedRowIndex = -1
@@ -3,7 +3,7 @@
import { deepEqual } from 'fast-equals'
import { getContext } from 'svelte'
import type { AppInput, EvalAppInput, UploadAppInput } from '../../inputType'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import { accessPropertyByPath } from '../../utils'
type T = string | number | boolean | Record<string | number, any> | undefined
@@ -23,7 +23,7 @@
}
}
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
$: state = $worldStore?.state
@@ -4,7 +4,7 @@
import { fade } from 'svelte/transition'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import InputValue from './InputValue.svelte'
export let componentInput: AppInput
@@ -13,7 +13,7 @@
export let render: boolean
// Sync the result to the output
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
$: outputs = $worldStore?.outputsById[id] as {
loading: Output<boolean>
@@ -3,11 +3,11 @@
import { faRefresh } from '@fortawesome/free-solid-svg-icons'
import { RefreshCcw, RefreshCw } from 'lucide-svelte'
import { getContext } from 'svelte'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
export let componentId: string
const { runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { runnableComponents, worldStore } = getContext<AppViewerContext>('AppViewerContext')
async function refresh() {
window.dispatchEvent(new Event('pointerup'))
@@ -1,11 +1,11 @@
<script lang="ts">
import { getContext } from 'svelte'
import { findGridItem } from '../../editor/appUtils'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
export let id: string
export let shouldWrap: boolean = false
const { app, breakpoint } = getContext<AppEditorContext>('AppEditorContext')
const { app, breakpoint } = getContext<AppViewerContext>('AppViewerContext')
$: gridItem = findGridItem($app, id)
@@ -12,7 +12,7 @@
import { fade } from 'svelte/transition'
import type { AppInputs, Runnable } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import InputValue from './InputValue.svelte'
import RefreshButton from './RefreshButton.svelte'
@@ -43,7 +43,7 @@
errorByComponent,
mode,
stateId
} = getContext<AppEditorContext>('AppEditorContext')
} = getContext<AppViewerContext>('AppViewerContext')
onMount(() => {
if (autoRefresh) {
@@ -1,7 +1,7 @@
<script lang="ts">
import { getContext, onMount } from 'svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import { isScriptByNameDefined, isScriptByPathDefined } from '../../utils'
import NonRunnableComponent from './NonRunnableComponent.svelte'
import RunnableComponent from './RunnableComponent.svelte'
@@ -22,7 +22,7 @@
export let gotoNewTab: boolean | undefined = undefined
export let render: boolean
const { staticExporter, noBackend } = getContext<AppEditorContext>('AppEditorContext')
const { staticExporter, noBackend } = getContext<AppViewerContext>('AppViewerContext')
$: if (initializing && result) {
initializing = false
@@ -3,7 +3,7 @@
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -15,7 +15,7 @@
export let customCss: ComponentCustomCSS<'text'> | undefined = undefined
export let render: boolean
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
export const staticOutputs: string[] = ['result']
@@ -3,7 +3,7 @@
import { twMerge } from 'tailwind-merge'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -16,7 +16,7 @@
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
export let render: boolean
const { app, worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
let labelValue: string = 'Title'
let minValue: string = ''
let maxValue: string = ''
@@ -4,7 +4,7 @@
import { FileInput } from '../../../common'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
@@ -14,7 +14,7 @@
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
export let render: boolean
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
let acceptedFileTypes: string[] | undefined = undefined
let allowMultiple: boolean | undefined = undefined
@@ -3,7 +3,7 @@
import Select from 'svelte-select'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -18,7 +18,7 @@
export let render: boolean
const { app, worldStore, connectingInput, selectedComponent } =
getContext<AppEditorContext>('AppEditorContext')
getContext<AppViewerContext>('AppViewerContext')
let items: { label: string; value: string }[]
let placeholder: string = 'Select an item'
@@ -3,7 +3,7 @@
import { twMerge } from 'tailwind-merge'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -15,7 +15,7 @@
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
export let render: boolean
const { app, worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
let defaultValue: number | undefined = undefined
let placeholder: string | undefined = undefined
@@ -3,7 +3,7 @@
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import { concatCustomCss } from '../../utils'
@@ -17,7 +17,7 @@
undefined
export let render: boolean
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
let min = 0
let max = 42
let step = 1
@@ -3,7 +3,7 @@
import Select from 'svelte-select'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -18,7 +18,7 @@
export let render: boolean
const { app, worldStore, connectingInput, selectedComponent } =
getContext<AppEditorContext>('AppEditorContext')
getContext<AppViewerContext>('AppViewerContext')
let items: { label: string; value: any; created?: boolean }[]
let placeholder: string = 'Select an item'
@@ -43,6 +43,8 @@
: []
if (defaultValue) {
value = JSON.stringify(defaultValue)
} else if (listItems.length > 0) {
value = listItems[0]?.value
}
}
@@ -3,7 +3,7 @@
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
import { twMerge } from 'tailwind-merge'
@@ -17,7 +17,7 @@
undefined
export let render: boolean
const { app, worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
let min = 0
let max = 42
let step = 1
@@ -3,7 +3,7 @@
import { twMerge } from 'tailwind-merge'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
@@ -18,7 +18,7 @@
'textinputcomponent'
export let render: boolean
const { app, worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
let placeholder: string | undefined = undefined
let defaultValue: string | undefined = undefined
@@ -3,7 +3,7 @@
import { twMerge } from 'tailwind-merge'
import type { AppInput } from '../../../inputType'
import type { Output } from '../../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../../types'
import { concatCustomCss } from '../../../utils'
import AlignWrapper from '../../helpers/AlignWrapper.svelte'
import InputValue from '../../helpers/InputValue.svelte'
@@ -16,7 +16,7 @@
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
export let render: boolean
const { app, worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
let defaultValue: number | undefined = undefined
@@ -2,7 +2,7 @@
import { getContext } from 'svelte'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS, GridItem } from '../../types'
import type { AppViewerContext, ComponentCustomCSS, GridItem } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
@@ -15,7 +15,7 @@
let noPadding: boolean | undefined = undefined
export const staticOutputs: string[] = []
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { app, focusedGrid, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
function onFocus() {
$focusedGrid = {
@@ -3,7 +3,7 @@
import { twMerge } from 'tailwind-merge'
import type { AppInput } from '../../inputType'
import type {
AppEditorContext,
AppViewerContext,
ComponentCustomCSS,
HorizontalAlignment,
VerticalAlignment
@@ -20,7 +20,7 @@
export let position: 'horizontal' | 'vertical'
export let render: boolean
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
let size = 2
let color = '#00000060'
@@ -2,7 +2,7 @@
import { getContext } from 'svelte'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import InputValue from '../helpers/InputValue.svelte'
import Portal from 'svelte-portal'
import { concatCustomCss } from '../../utils'
@@ -18,7 +18,7 @@
export let noWFull = false
export let render: boolean
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { app, focusedGrid, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
let gridContent: string[] | undefined = undefined
let noPadding: boolean | undefined = undefined
@@ -2,7 +2,7 @@
import { getContext } from 'svelte'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppEditorContext, AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
import { Pane, Splitpanes } from 'svelte-splitpanes'
@@ -16,8 +16,9 @@
export let panes: number[]
export let render: boolean
const { app, focusedGrid, selectedComponent, componentControl } =
getContext<AppEditorContext>('AppEditorContext')
const { app, focusedGrid, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
const { componentControl } = getContext<AppEditorContext>('AppEditorContext')
let noPadding: boolean | undefined = undefined
@@ -4,7 +4,7 @@
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import type { AppEditorContext, AppViewerContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
@@ -18,8 +18,10 @@
export let render: boolean
export const staticOutputs: string[] = ['selectedTabIndex']
const { app, worldStore, focusedGrid, selectedComponent, componentControl } =
getContext<AppEditorContext>('AppEditorContext')
const { app, worldStore, focusedGrid, selectedComponent } =
getContext<AppViewerContext>('AppViewerContext')
const { componentControl } = getContext<AppEditorContext>('AppEditorContext')
let selected: string = tabs[0]
let noPadding: boolean | undefined = undefined
@@ -4,11 +4,12 @@
import { twMerge } from 'tailwind-merge'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import { writable } from 'svelte/store'
import { writable, type Writable } from 'svelte/store'
import { buildWorld, type World } from '../rx'
import type {
App,
AppEditorContext,
AppViewerContext,
ConnectingInput,
EditorBreakpoint,
EditorMode,
@@ -32,11 +33,13 @@
import SettingsPanel from './SettingsPanel.svelte'
import { fly } from 'svelte/transition'
import type { Policy } from '$lib/gen'
import { VariableService, type Policy } from '$lib/gen'
import { page } from '$app/stores'
import CssSettings from './componentsPanel/CssSettings.svelte'
import { initHistory } from '$lib/history'
import ComponentNavigation from './component/ComponentNavigation.svelte'
import ItemPicker from '$lib/components/ItemPicker.svelte'
import VariableEditor from '$lib/components/VariableEditor.svelte'
export let app: App
export let path: string
@@ -62,8 +65,9 @@
const runnableComponents = writable<Record<string, () => Promise<void>>>({})
const errorByComponent = writable<Record<string, { error: string; componentId: string }>>({})
const focusedGrid = writable<FocusedGrid | undefined>(undefined)
const pickVariableCallback: Writable<((path: string) => void) | undefined> = writable(undefined)
setContext<AppEditorContext>('AppEditorContext', {
setContext<AppViewerContext>('AppViewerContext', {
worldStore,
staticOutputs,
app: appStore,
@@ -84,9 +88,13 @@
openDebugRun: writable(undefined),
focusedGrid,
stateId: writable(0),
parentWidth: writable(0),
parentWidth: writable(0)
})
setContext<AppEditorContext>('AppEditorContext', {
history,
componentControl: writable({})
componentControl: writable({}),
pickVariableCallback
})
let timeout: NodeJS.Timeout | undefined = undefined
@@ -132,6 +140,14 @@
} else {
selectedTab = 'insert'
}
let itemPicker: ItemPicker | undefined = undefined
$: if ($pickVariableCallback) {
itemPicker?.openDrawer()
}
let variableEditor: VariableEditor | undefined = undefined
</script>
<svelte:window on:hashchange={hashchange} />
@@ -284,3 +300,36 @@
{:else}
App editor not available to operators
{/if}
<ItemPicker
bind:this={itemPicker}
pickCallback={(path, _) => {
$pickVariableCallback?.(path)
}}
itemName="Variable"
extraField="path"
loadItems={async () =>
(await VariableService.listVariable({ workspace: $workspaceStore ?? '' })).map((x) => ({
name: x.path,
...x
}))}
>
<div
slot="submission"
class="flex flex-row-reverse w-full bg-white border-t border-gray-200 rounded-bl-lg rounded-br-lg"
>
<Button
variant="border"
color="blue"
size="sm"
startIcon={{ icon: faPlus }}
on:click={() => {
variableEditor?.initNew?.()
}}
>
New variable
</Button>
</div>
</ItemPicker>
<VariableEditor bind:this={variableEditor} />
@@ -27,7 +27,7 @@
import Tooltip from '$lib/components/Tooltip.svelte'
import { AppService, Job, Policy } from '$lib/gen'
import { redo, undo } from '$lib/history'
import { userStore, workspaceStore } from '$lib/stores'
import { workspaceStore } from '$lib/stores'
import {
faBug,
faClipboard,
@@ -61,7 +61,7 @@
StaticAppInput,
UserAppInput
} from '../inputType'
import type { AppEditorContext } from '../types'
import type { AppEditorContext, AppViewerContext } from '../types'
import { allItems, toStatic } from '../utils'
import AppExportButton from './AppExportButton.svelte'
import AppInputs from './AppInputs.svelte'
@@ -90,9 +90,10 @@
errorByComponent,
openDebugRun,
focusedGrid,
selectedComponent,
history
} = getContext<AppEditorContext>('AppEditorContext')
selectedComponent
} = getContext<AppViewerContext>('AppViewerContext')
const { history } = getContext<AppEditorContext>('AppEditorContext')
const loading = {
publish: false,
@@ -470,8 +471,8 @@
<input type="text" placeholder="App summary" class="text-sm w-full" bind:value={$summary} />
</div>
<UndoRedo
undoProps={{ disabled: $history.index === 0 }}
redoProps={{ disabled: $history.index === $history.history.length - 1 }}
undoProps={{ disabled: $history?.index === 0 }}
redoProps={{ disabled: $history && $history?.index === $history.history.length - 1 }}
on:undo={() => {
$app = undo(history, $app)
}}
@@ -2,11 +2,11 @@
import { Alert } from '$lib/components/common'
import Toggle from '$lib/components/Toggle.svelte'
import { getContext } from 'svelte'
import type { AppEditorContext } from '../types'
import type { AppViewerContext } from '../types'
import AppComponentInput from './AppComponentInput.svelte'
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
let resourceOnly: boolean = true
</script>
@@ -5,6 +5,7 @@
import type {
App,
AppEditorContext,
AppViewerContext,
ConnectingInput,
EditorBreakpoint,
EditorMode
@@ -40,7 +41,7 @@
const runnableComponents = writable<Record<string, () => Promise<void>>>({})
setContext<AppEditorContext>('AppEditorContext', {
setContext<AppViewerContext>('AppViewerContext', {
worldStore,
staticOutputs,
app: appStore,
@@ -61,9 +62,13 @@
openDebugRun: writable(undefined),
focusedGrid: writable(undefined),
stateId: writable(0),
parentWidth: writable(0),
history: writable<any>(undefined),
componentControl: writable({})
parentWidth: writable(0)
})
setContext<AppEditorContext>('AppEditorContext', {
history: undefined,
componentControl: writable({}),
pickVariableCallback: writable(undefined)
})
let mounted = false
@@ -1,6 +1,6 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import type { AppEditorContext } from '../types'
import type { AppViewerContext } from '../types'
import { Anchor, Bug, Expand, Move } from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import Popover from '$lib/components/Popover.svelte'
@@ -15,7 +15,7 @@
const dispatch = createEventDispatcher()
const { errorByComponent, openDebugRun } = getContext<AppEditorContext>('AppEditorContext')
const { errorByComponent, openDebugRun } = getContext<AppViewerContext>('AppViewerContext')
$: error = Object.values($errorByComponent).find((e) => e.componentId === component.id)
@@ -56,15 +56,15 @@
{/if}
</button>
<button
title="Position locking"
title="Expand"
class={classNames(
'text-gray-800 px-1 text-2xs py-0.5 font-bold w-fit shadow border border-gray-300 absolute -top-1 right-[0.5rem] z-50 cursor-pointer',
'text-gray-800 px-1 text-2xs py-0.5 font-bold w-fit shadow border border-gray-300 absolute -top-1 right-[4.5rem] z-50 cursor-pointer',
' hover:bg-gray-300',
selected ? 'bg-gray-200/80' : 'bg-gray-200/80'
)}
on:click={() => dispatch('expand')}
>
<Expand aria-label="Lock position" size={14} />
<Expand aria-label="Expand position" size={14} />
</button>
{/if}
@@ -73,7 +73,7 @@
title="Move"
on:mousedown|stopPropagation|capture
class={classNames(
'text-gray-600 px-1 text-2xs py-0.5 font-bold rounded-t-sm w-fit absolute border border-gray-300 -top-1 shadow right-[4.5rem] z-50 cursor-move',
'text-gray-600 px-1 text-2xs py-0.5 font-bold rounded-t-sm w-fit absolute border border-gray-300 -top-1 shadow right-[0.5rem] z-50 cursor-move',
'bg-gray-200/80'
)}
>
@@ -1,6 +1,6 @@
<script lang="ts">
import { getContext, afterUpdate } from 'svelte'
import type { App, AppEditorContext, GridItem } from '../types'
import { getContext, afterUpdate, setContext } from 'svelte'
import type { App, AppEditorContext, AppViewerContext, GridItem } from '../types'
import Grid from '@windmill-labs/svelte-grid'
import { classNames } from '$lib/utils'
import { columnConfiguration, disableDrag, enableDrag, isFixed, toggleFixed } from '../gridUtils'
@@ -11,7 +11,7 @@
import HiddenComponent from '../components/helpers/HiddenComponent.svelte'
import Component from './component/Component.svelte'
import { deepEqual } from 'fast-equals'
import { push } from '$lib/history'
import { push, type History } from '$lib/history'
import { expandGriditem, findGridItem } from './appUtils'
export let policy: Policy
@@ -26,9 +26,10 @@
summary,
focusedGrid,
parentWidth,
history,
breakpoint
} = getContext<AppEditorContext>('AppEditorContext')
} = getContext<AppViewerContext>('AppViewerContext')
const { history } = getContext<AppEditorContext>('AppEditorContext')
// The drag is disabled when the user is connecting an input
// or when the user is previewing the app
@@ -107,13 +108,17 @@
let pointerdown = false
let lastapp: App | undefined = undefined
const onpointerdown = () => {
lastapp = JSON.parse(JSON.stringify($app))
pointerdown = true
if (history) {
lastapp = JSON.parse(JSON.stringify($app))
pointerdown = true
}
}
const onpointerup = () => {
pointerdown = false
if (!deepEqual(lastapp, $app)) {
push(history, lastapp, false, true)
if (history) {
pointerdown = false
if (!deepEqual(lastapp, $app)) {
push(history, lastapp, false, true)
}
}
}
@@ -1,9 +1,9 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppEditorContext, GridItem } from '../types'
import type { AppViewerContext, GridItem } from '../types'
import ComponentPanel from './settingsPanel/ComponentPanel.svelte'
const { selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
export let gridItems: GridItem[]
export let parent: string | undefined
</script>
@@ -3,9 +3,9 @@
import { ChevronDown, RefreshCw } from 'lucide-svelte'
import { getContext, onMount } from 'svelte'
import Button from '../../common/button/Button.svelte'
import type { AppEditorContext } from '../types'
import type { AppViewerContext } from '../types'
const { runnableComponents } = getContext<AppEditorContext>('AppEditorContext')
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
let loading: boolean = false
let timeout: NodeJS.Timer | undefined = undefined
let interval: number | undefined = undefined
@@ -14,14 +14,14 @@
$: componentNumber = Object.keys($runnableComponents).length
function onClick(stopAfterClear = true) {
if(timeout) {
if (timeout) {
clearInterval(timeout)
timeout = undefined
shouldRefresh = false
if(stopAfterClear) return;
if (stopAfterClear) return
}
refresh()
if(interval) {
if (interval) {
shouldRefresh = true
timeout = setInterval(refresh, interval)
}
@@ -44,12 +44,12 @@
}
function visChange() {
if(document.visibilityState === 'hidden') {
if(timeout) {
if (document.visibilityState === 'hidden') {
if (timeout) {
clearInterval(timeout)
timeout = undefined
}
} else if(shouldRefresh) {
} else if (shouldRefresh) {
timeout = setInterval(refresh, interval)
}
}
@@ -58,7 +58,7 @@
document.addEventListener('visibilitychange', visChange)
return () => {
document.removeEventListener('visibilitychange', visChange)
if(timeout) clearInterval(timeout)
if (timeout) clearInterval(timeout)
}
})
</script>
@@ -66,17 +66,19 @@
<div class="flex items-center">
<Button
on:click={() => onClick()}
color="{timeout ? 'blue' : 'light'}"
variant="{timeout ? 'contained' : 'border'}"
color={timeout ? 'blue' : 'light'}
variant={timeout ? 'contained' : 'border'}
size="xs"
btnClasses="!rounded-r-none {timeout ? '!border !border-blue-500' : ''}"
title="Refresh {componentNumber} component{componentNumber > 1 ? 's' : ''} {interval ? `every ${interval / 1000} seconds` : 'once'}"
title="Refresh {componentNumber} component{componentNumber > 1 ? 's' : ''} {interval
? `every ${interval / 1000} seconds`
: 'once'}"
>
<RefreshCw class={loading ? 'animate-spin' : ''} size={16} />
</Button>
<Dropdown
btnClasses="!rounded-l-none !border-l-0 min-w-[4rem] !px-2"
color="{timeout ? 'blue' : 'light'}"
color={timeout ? 'blue' : 'light'}
variant="border"
dropdownItems={[
{
@@ -86,7 +88,7 @@
...[1, 2, 3, 4, 5, 6].map((i) => ({
displayName: `Every ${i * 5} seconds`,
action: () => setInter(i * 5000)
})),
}))
]}
>
<span class="grow text center">{interval ? `${interval / 1000}s` : 'once'}</span>
@@ -1,11 +1,11 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppEditorContext } from '../types'
import type { AppViewerContext } from '../types'
import GridPanel from './GridPanel.svelte'
import PanelSection from './settingsPanel/common/PanelSection.svelte'
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
const { selectedComponent, app, stateId } = getContext<AppEditorContext>('AppEditorContext')
const { selectedComponent, app, stateId } = getContext<AppViewerContext>('AppViewerContext')
</script>
{#if $app.grid}
@@ -4,7 +4,7 @@
import Grid from '@windmill-labs/svelte-grid'
import { twMerge } from 'tailwind-merge'
import { columnConfiguration, isFixed, toggleFixed } from '../gridUtils'
import type { AppEditorContext, GridItem } from '../types'
import type { AppEditorContext, AppViewerContext, GridItem } from '../types'
import Component from './component/Component.svelte'
import { expandGriditem, findGridItem } from './appUtils'
import { push } from '$lib/history'
@@ -22,16 +22,10 @@
const dispatch = createEventDispatcher()
const {
app,
connectingInput,
selectedComponent,
focusedGrid,
mode,
parentWidth,
breakpoint,
history
} = getContext<AppEditorContext>('AppEditorContext')
const { app, connectingInput, selectedComponent, focusedGrid, mode, parentWidth, breakpoint } =
getContext<AppViewerContext>('AppViewerContext')
const { history } = getContext<AppEditorContext>('AppEditorContext')
$: highlight = id === $focusedGrid?.parentComponentId && shouldHighlight
@@ -3,7 +3,7 @@
import { fade } from 'svelte/transition'
import { Loader2 } from 'lucide-svelte'
import { twMerge } from 'tailwind-merge'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import ComponentHeader from '../ComponentHeader.svelte'
import type { AppComponent } from './components'
import {
@@ -49,7 +49,7 @@
export let render: boolean
const { staticOutputs, mode, connectingInput, app, errorByComponent } =
getContext<AppEditorContext>('AppEditorContext')
getContext<AppViewerContext>('AppViewerContext')
let hover = false
let initializing: boolean | undefined = undefined
let componentContainerHeight: number = 0
@@ -222,7 +222,7 @@
recomputeIds={component.recomputeIds}
{render}
/>
{:else if component.type === 'selectcomponent'}
{:else if component.type === 'selectcomponent' || component.type === 'resourceselectcomponent'}
<AppSelect
id={component.id}
verticalAlignment={component.verticalAlignment}
@@ -1,10 +1,12 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppEditorContext, EditorBreakpoint, GridItem } from '../../types'
import type { AppEditorContext, AppViewerContext, EditorBreakpoint, GridItem } from '../../types'
import { findGridItemParentId } from '../appUtils'
const { app, selectedComponent, breakpoint, componentControl, focusedGrid } =
getContext<AppEditorContext>('AppEditorContext')
const { app, selectedComponent, breakpoint, focusedGrid } =
getContext<AppViewerContext>('AppViewerContext')
const { componentControl } = getContext<AppEditorContext>('AppEditorContext')
function getSortedGridItemsOfChildren(): GridItem[] {
if (!$focusedGrid) {
@@ -74,7 +74,8 @@ export type AggridComponent = BaseComponent<'aggridcomponent'>
export type DisplayComponent = BaseComponent<'displaycomponent'>
export type ImageComponent = BaseComponent<'imagecomponent'>
export type InputComponent = BaseComponent<'inputcomponent'>
export type SelectComponent = BaseComponent<'selectcomponent'>
export type SelectComponent = BaseComponent<'resourceselectcomponent'>
export type ResourceSelectComponent = BaseComponent<'selectcomponent'>
export type MultiSelectComponent = BaseComponent<'multiselectcomponent'>
export type CheckboxComponent = BaseComponent<'checkboxcomponent'>
export type RadioComponent = BaseComponent<'radiocomponent'>
@@ -116,6 +117,7 @@ export type AppComponent = BaseAppComponent &
| PieChartComponent
| ScatterChartComponent
| SelectComponent
| ResourceSelectComponent
| MultiSelectComponent
| CheckboxComponent
| FormComponent
@@ -911,7 +913,7 @@ Hello \${ctx.username}
}
},
multiselectcomponent: {
name: 'MultiSelect',
name: 'Multi Select',
icon: List,
dims: '2:1-3:1',
data: {
@@ -940,6 +942,36 @@ Hello \${ctx.username}
softWrap: true
}
},
resourceselectcomponent: {
name: 'Resource Select',
icon: List,
dims: '2:1-3:1',
data: {
verticalAlignment: 'center',
id: '',
type: 'resourceselectcomponent',
componentInput: undefined,
configuration: {
items: {
type: 'static',
fieldType: 'array',
subFieldType: 'labeledresource',
value: []
},
placeholder: {
type: 'static',
fieldType: 'text',
value: 'Select an item',
onlyStatic: true
}
},
customCss: {
input: { style: '' }
} as const,
card: false,
softWrap: true
}
},
numberinputcomponent: {
name: 'Number',
icon: Binary,
@@ -72,14 +72,14 @@ return {
},
displaycomponent: {
deno: `export async function main() {
return {
"foo": 42
}
return {
"foo": 42
}
}`,
python3: `def main():
return {
"foo": 42
}`
return {
"foo": 42
}`
},
htmlcomponent: {
deno: `export async function main() {
@@ -32,6 +32,7 @@ const inputs: ComponentSet = {
'fileinputcomponent',
'checkboxcomponent',
'selectcomponent',
'resourceselectcomponent',
'multiselectcomponent',
]
} as const
@@ -1,5 +1,5 @@
<script lang="ts">
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, AppViewerContext } from '../../types'
import { getContext } from 'svelte'
import { fade, slide } from 'svelte/transition'
import { dirtyStore } from '$lib/components/common/confirmationModal/dirtyStore'
@@ -10,8 +10,9 @@
import { push } from '$lib/history'
import { flip } from 'svelte/animate'
const { app, selectedComponent, focusedGrid, history } =
getContext<AppEditorContext>('AppEditorContext')
const { app, selectedComponent, focusedGrid } = getContext<AppViewerContext>('AppViewerContext')
const { history } = getContext<AppEditorContext>('AppEditorContext')
function addComponent(appComponentType: AppComponent['type']): void {
push(history, $app)
@@ -4,7 +4,7 @@
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
import { emptyString } from '$lib/utils'
import { Tab, TabContent, Tabs } from '../../../common'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import ListItem from './ListItem.svelte'
import { isOpenStore } from './store'
import CssProperty from './CssProperty.svelte'
@@ -24,7 +24,7 @@
ids: string[]
}
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
let rawCode = ''
let viewJsonSchema = false
@@ -1,12 +1,12 @@
<script lang="ts">
import { components } from '../component'
import { getContext } from 'svelte'
import type { AppEditorContext, GridItem } from '../../types'
import type { AppViewerContext, GridItem } from '../../types'
import ComponentOutputViewer from './ComponentOutputViewer.svelte'
export let gridItem: GridItem
const { app, staticOutputs, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { app, staticOutputs, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
const name = getComponentNameById(gridItem.id)
function getComponentNameById(componentId: string) {
@@ -2,12 +2,12 @@
import ObjectViewer from '$lib/components/propertyPicker/ObjectViewer.svelte'
import { getContext } from 'svelte'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
export let outputs: string[] = []
export let componentId: string
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
let object = {}
@@ -1,18 +1,15 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import { X } from 'lucide-svelte'
import { getContext } from 'svelte'
import { flip } from 'svelte/animate'
import { fade, slide } from 'svelte/transition'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import { findGridItem } from '../appUtils'
import { components } from '../component'
import PanelSection from '../settingsPanel/common/PanelSection.svelte'
import ComponentOutput from './ComponentOutput.svelte'
import ComponentOutputViewer from './ComponentOutputViewer.svelte'
const { connectingInput, staticOutputs, worldStore, selectedComponent, app } =
getContext<AppEditorContext>('AppEditorContext')
getContext<AppViewerContext>('AppViewerContext')
const manualyOpenedIds = new Set<string>()
@@ -11,7 +11,7 @@
import { Building, Globe2 } from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import { fly } from 'svelte/transition'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import { defaultCode } from '../component'
import InlineScriptList from '../settingsPanel/mainInput/InlineScriptList.svelte'
import WorkspaceScriptList from '../settingsPanel/mainInput/WorkspaceScriptList.svelte'
@@ -23,7 +23,7 @@
let filter: string = ''
let picker: Drawer
const { appPath, app } = getContext<AppEditorContext>('AppEditorContext')
const { appPath, app } = getContext<AppViewerContext>('AppViewerContext')
const dispatch = createEventDispatcher()
async function inferInlineScriptSchema(
@@ -2,7 +2,7 @@
import Button from '$lib/components/common/button/Button.svelte'
import type { Preview } from '$lib/gen'
import { createEventDispatcher, getContext, onMount } from 'svelte'
import type { AppEditorContext, InlineScript } from '../../types'
import type { AppViewerContext, InlineScript } from '../../types'
import { CornerDownLeft, Maximize2, Plus, Trash2, X } from 'lucide-svelte'
import InlineScriptEditorDrawer from './InlineScriptEditorDrawer.svelte'
import { inferArgs } from '$lib/infer'
@@ -26,7 +26,7 @@
export let fields: Record<string, AppInput> = {}
export let syncFields: boolean = false
const { runnableComponents, stateId } = getContext<AppEditorContext>('AppEditorContext')
const { runnableComponents, stateId } = getContext<AppViewerContext>('AppViewerContext')
let editor: Editor
let validCode = true
@@ -1,6 +1,6 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import SplitPanesWrapper from '$lib/components/splitPanes/SplitPanesWrapper.svelte'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import InlineScriptsPanelList from './InlineScriptsPanelList.svelte'
@@ -9,7 +9,7 @@
import InlineScriptEditorPanel from './InlineScriptEditorPanel.svelte'
const { app, staticOutputs, runnableComponents } =
getContext<AppEditorContext>('AppEditorContext')
getContext<AppViewerContext>('AppViewerContext')
let selectedScriptComponentId: string | undefined = undefined
@@ -3,7 +3,7 @@
import { Plus } from 'lucide-svelte'
import { getContext } from 'svelte'
import Tooltip from '../../../Tooltip.svelte'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import { getAllScriptNames } from '../../utils'
import PanelSection from '../settingsPanel/common/PanelSection.svelte'
import { getAppScripts } from './utils'
@@ -11,7 +11,7 @@
const PREFIX = 'script-selector-' as const
export let selectedScriptComponentId: string | undefined = undefined
const { app, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
const { app, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
function selectScript(id: string) {
selectedScriptComponentId = id
@@ -22,6 +22,8 @@
componentInput.value.push(0)
} else if (componentInput.subFieldType === 'object') {
componentInput.value.push({})
} else if (componentInput.subFieldType === 'labeledresource') {
componentInput.value.push({ value: '', label: '' })
} else if (
componentInput.subFieldType === 'text' ||
componentInput.subFieldType === 'textarea' ||
@@ -3,12 +3,12 @@
import { faArrowRight, faCode, faPen } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
export let componentInput: AppInput
export let disableStatic: boolean = false
const { onchange } = getContext<AppEditorContext>('AppEditorContext')
const { onchange } = getContext<AppViewerContext>('AppViewerContext')
$: if (componentInput.fieldType == 'template' && componentInput.type == 'static') {
//@ts-ignore
@@ -2,7 +2,7 @@
import Button from '$lib/components/common/button/Button.svelte'
import { faChevronDown, faChevronUp, faCopy, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { AppEditorContext } from '../../types'
import type { AppEditorContext, AppViewerContext } from '../../types'
import PanelSection from './common/PanelSection.svelte'
import InputsSpecsEditor from './InputsSpecsEditor.svelte'
import TableActions from './TableActions.svelte'
@@ -42,9 +42,10 @@
selectedComponent,
worldStore,
focusedGrid,
stateId,
history
} = getContext<AppEditorContext>('AppEditorContext')
stateId
} = getContext<AppViewerContext>('AppViewerContext')
const { history } = getContext<AppEditorContext>('AppEditorContext')
function duplicateElement(id: string) {
push(history, $app)
@@ -2,7 +2,7 @@
import Button from '$lib/components/common/button/Button.svelte'
import { faPlus, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import { deleteGridItem } from '../appUtils'
import type { AppComponent } from '../component'
import PanelSection from './common/PanelSection.svelte'
@@ -11,7 +11,7 @@
export let component: AppComponent
const { app, staticOutputs, runnableComponents } =
getContext<AppEditorContext>('AppEditorContext')
getContext<AppViewerContext>('AppViewerContext')
function addTab() {
const numberOfPanes = panes.length
@@ -2,7 +2,7 @@
import Button from '$lib/components/common/button/Button.svelte'
import { faPlus, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import { deleteGridItem } from '../appUtils'
import type { AppComponent } from '../component'
import PanelSection from './common/PanelSection.svelte'
@@ -11,7 +11,7 @@
export let component: AppComponent
const { app, staticOutputs, runnableComponents, focusedGrid } =
getContext<AppEditorContext>('AppEditorContext')
getContext<AppViewerContext>('AppViewerContext')
function addTab() {
const numberOfTabs = tabs.length
@@ -1,4 +1,5 @@
<script lang="ts">
import VariableEditor from '$lib/components/VariableEditor.svelte'
import type {
ConnectedAppInput,
EvalAppInput,
@@ -5,7 +5,7 @@
import { fieldTypeToTsType } from '../../utils'
import InputsSpecEditor from './InputsSpecEditor.svelte'
import { getContext } from 'svelte'
import type { AppEditorContext, BaseAppComponent } from '../../types'
import type { AppViewerContext, BaseAppComponent } from '../../types'
import Tooltip from '$lib/components/Tooltip.svelte'
import Popover from '$lib/components/Popover.svelte'
@@ -16,7 +16,7 @@
export let rowColumns = false
export let resourceOnly = false
const { connectingInput } = getContext<AppEditorContext>('AppEditorContext')
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
</script>
{#if inputSpecs}
@@ -4,7 +4,7 @@
import { push } from '$lib/history'
import { faCopy } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { App, AppEditorContext } from '../../types'
import type { App, AppEditorContext, AppViewerContext } from '../../types'
import {
createNewGridItem,
deleteGridItem,
@@ -19,7 +19,8 @@
let selectedOption: string
const { app, history } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
const { history } = getContext<AppEditorContext>('AppEditorContext')
function listAllSubGrids(app: App) {
return app.subgrids ? Object.keys(app.subgrids) : []
@@ -1,13 +1,13 @@
<script lang="ts">
import Badge from '$lib/components/common/badge/Badge.svelte'
import { getContext } from 'svelte'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import PanelSection from './common/PanelSection.svelte'
export let recomputeIds: string[] | undefined = undefined
export let ownId: string
const { runnableComponents } = getContext<AppEditorContext>('AppEditorContext')
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
function onChange(
event: Event & {
@@ -4,11 +4,11 @@
import { faClose, faEdit } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { ResultAppInput } from '../../inputType'
import type { AppEditorContext } from '../../types'
import type { AppViewerContext } from '../../types'
import { clearResultAppInput } from '../../utils'
import InlineScriptEditorDrawer from '../inlineScriptsPanel/InlineScriptEditorDrawer.svelte'
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app } = getContext<AppViewerContext>('AppViewerContext')
export let appInput: ResultAppInput
let inlineScriptEditorDrawer: InlineScriptEditorDrawer
@@ -6,7 +6,7 @@
import { faPlus, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import { Icon } from 'svelte-awesome'
import type { AppEditorContext, BaseAppComponent } from '../../types'
import type { AppViewerContext, BaseAppComponent } from '../../types'
import type { ButtonComponent } from '../component'
import PanelSection from './common/PanelSection.svelte'
import TableActionLabel from './TableActionLabel.svelte'
@@ -14,7 +14,7 @@
export let components: (BaseAppComponent & ButtonComponent)[]
export let id: string
const { selectedComponent, staticOutputs } = getContext<AppEditorContext>('AppEditorContext')
const { selectedComponent, staticOutputs } = getContext<AppViewerContext>('AppViewerContext')
function addComponent() {
const actionId = getNextId(components.map((x) => x.id.split('_')[1]))
@@ -1,5 +1,5 @@
<script lang="ts">
import type { AppEditorContext } from '../../../types'
import type { AppViewerContext } from '../../../types'
import { Badge, Button } from '$lib/components/common'
import { faArrowRight, faClose } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
@@ -7,7 +7,7 @@
export let componentInput: ConnectedAppInput
const { connectingInput } = getContext<AppEditorContext>('AppEditorContext')
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
function applyConnection() {
if (
@@ -2,7 +2,7 @@
import type { EvalAppInput } from '../../../inputType'
import { getContext } from 'svelte'
import type { AppEditorContext } from '$lib/components/apps/types'
import type { AppViewerContext } from '$lib/components/apps/types'
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
import { buildExtraLib } from '$lib/components/apps/utils'
@@ -10,7 +10,7 @@
export let id: string
export let hasRows: boolean = false
const { onchange, worldStore } = getContext<AppEditorContext>('AppEditorContext')
const { onchange, worldStore } = getContext<AppViewerContext>('AppViewerContext')
$: componentInput && onchange?.()
@@ -6,28 +6,31 @@
import ResourcePicker from '$lib/components/ResourcePicker.svelte'
import JsonEditor from './JsonEditor.svelte'
import { getContext } from 'svelte'
import type { AppEditorContext } from '$lib/components/apps/types'
import type { AppEditorContext, AppViewerContext } from '$lib/components/apps/types'
import IconSelectInput from './IconSelectInput.svelte'
import ColorInput from './ColorInput.svelte'
import { Icon } from 'svelte-awesome'
import { faDollarSign } from '@fortawesome/free-solid-svg-icons'
export let componentInput: StaticAppInput | undefined
const { onchange } = getContext<AppEditorContext>('AppEditorContext')
const { onchange } = getContext<AppViewerContext>('AppViewerContext')
const { pickVariableCallback } = getContext<AppEditorContext>('AppEditorContext')
$: componentInput && onchange?.()
</script>
{#if componentInput?.type === 'static'}
{#if componentInput.fieldType === 'number'}
<input type="number" bind:value={componentInput.value} />
<input on:keydown|stopPropagation type="number" bind:value={componentInput.value} />
{:else if componentInput.fieldType === 'textarea'}
<textarea bind:value={componentInput.value} />
<textarea on:keydown|stopPropagation bind:value={componentInput.value} />
{:else if componentInput.fieldType === 'date'}
<input type="date" bind:value={componentInput.value} />
<input on:keydown|stopPropagation type="date" bind:value={componentInput.value} />
{:else if componentInput.fieldType === 'boolean'}
<Toggle bind:checked={componentInput.value} />
{:else if componentInput.fieldType === 'select'}
<select bind:value={componentInput.value}>
<select on:keydown|stopPropagation on:keydown|stopPropagation bind:value={componentInput.value}>
{#each staticValues[componentInput.optionValuesKey] || [] as option}
<option value={option}>
{option}
@@ -36,6 +39,24 @@
</select>
{:else if componentInput.fieldType === 'icon-select'}
<IconSelectInput bind:componentInput />
{:else if componentInput.fieldType === 'labeledresource'}
{#if componentInput?.value && typeof componentInput?.value == 'object' && 'label' in componentInput?.value}
<div class="flex flex-col gap-1 w-full">
<input placeholder="Label" type="text" bind:value={componentInput.value['label']} />
<ResourcePicker
initialValue={componentInput.value?.['value']?.split('$res:')[1] || ''}
on:change={(e) => {
let path = e.detail
if (componentInput && path) {
componentInput.value['value'] = `$res:${path}`
}
}}
/>
</div>
{:else}
Inconsistent labeled resource object
{/if}
{:else if componentInput.fieldType === 'color'}
<ColorInput bind:componentInput />
{:else if componentInput.fieldType === 'object'}
@@ -64,6 +85,25 @@
{:else if componentInput.fieldType === 'array'}
<ArrayStaticInputEditor bind:componentInput on:deleteArrayItem />
{:else}
<input type="text" placeholder="Static value" bind:value={componentInput.value} />
<div class="flex gap-1">
<input
on:keydown|stopPropagation
type="text"
placeholder="Static value"
bind:value={componentInput.value}
/>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="min-w-min min-h-[34px] items-center leading-4 px-3 text-gray-600 cursor-pointer border border-gray-700 rounded center-center"
on:click={() => {
$pickVariableCallback = (variable) => {
if (componentInput) {
componentInput.value = `$var:${variable}`
}
}
}}
><Icon data={faDollarSign} />
</div>
</div>
{/if}
{/if}
@@ -7,7 +7,7 @@
import type { ResultAppInput } from '$lib/components/apps/inputType'
import WorkspaceScriptList from './WorkspaceScriptList.svelte'
import WorkspaceFlowList from './WorkspaceFlowList.svelte'
import type { AppEditorContext, GridItem, InlineScript } from '$lib/components/apps/types'
import type { AppViewerContext, GridItem, InlineScript } from '$lib/components/apps/types'
import { getContext } from 'svelte'
import type { Schema } from '$lib/common'
import { getAllScriptNames, loadSchema, schemaToInputsSpec } from '$lib/components/apps/utils'
@@ -22,7 +22,7 @@
let filter: string = ''
let picker: Drawer
const { app, workspace } = getContext<AppEditorContext>('AppEditorContext')
const { app, workspace } = getContext<AppViewerContext>('AppViewerContext')
async function loadSchemaFromTriggerable(
path: string,
+11 -7
View File
@@ -17,6 +17,7 @@ export type InputType =
| 'object'
| 'array'
| 'any'
| 'labeledresource'
// Connection to an output of another component
// defined by the id of the component and the path of the output
@@ -139,11 +140,11 @@ export type AppInput =
| AppInputSpec<'object', Record<string | number, any>>
| AppInputSpec<'object', string>
| (AppInputSpec<'select', string> & {
/**
* One of the keys of `staticValues` from `lib/components/apps/editor/componentsPanel/componentStaticValues`
*/
optionValuesKey: keyof typeof staticValues
})
/**
* One of the keys of `staticValues` from `lib/components/apps/editor/componentsPanel/componentStaticValues`
*/
optionValuesKey: keyof typeof staticValues
})
| (AppInputSpec<'select', string> & StaticOptions)
| AppInputSpec<'icon-select', string>
| AppInputSpec<'color', string>
@@ -156,9 +157,12 @@ export type AppInput =
| AppInputSpec<'array', string[], 'datetime'>
| AppInputSpec<'array', object[], 'object'>
| (AppInputSpec<'array', string[], 'select'> & {
optionValuesKey: keyof typeof staticValues
})
optionValuesKey: keyof typeof staticValues
})
| (AppInputSpec<'array', string[], 'select'> & StaticOptions)
| AppInputSpec<'array', object[], 'labeledresource'>
| AppInputSpec<'labeledresource', object>
export type RowAppInput = Extract<AppInput, { type: 'row' }>
export type StaticAppInput = Extract<AppInput, { type: 'static' }>
+7 -2
View File
@@ -3,6 +3,7 @@ import type { Preview } from '$lib/gen'
import type { History } from '$lib/history'
import type { FilledItem } from '@windmill-labs/svelte-grid'
import type { Writable } from 'svelte/store'
import type VariableEditor from '../VariableEditor.svelte'
import type { AppComponent } from './editor/component/components'
import type {
AppInput,
@@ -111,7 +112,7 @@ export type ConnectingInput = {
hoveredComponent: string | undefined
}
export type AppEditorContext = {
export type AppViewerContext = {
worldStore: Writable<World | undefined>
staticOutputs: Writable<Record<string, string[]>>
app: Writable<App>
@@ -133,8 +134,12 @@ export type AppEditorContext = {
focusedGrid: Writable<FocusedGrid | undefined>
stateId: Writable<number>
parentWidth: Writable<number>
history: History<App>
}
export type AppEditorContext = {
history: History<App> | undefined
componentControl: Writable<Record<string, { left?: () => boolean; right?: () => boolean }>>
pickVariableCallback: Writable<((path: string) => void) | undefined>
}
export type FocusedGrid = { parentComponentId: string; subGridIndex: number }
+31 -22
View File
@@ -7,34 +7,43 @@ export function initHistory<T>(initial: T): History<T> {
return writable({ history: [initial], index: 0 })
}
export function undo<T>(history: History<T>, now: T): T {
let chistory = get(history)
if (chistory.index == 0) return now
if (!deepEqual(chistory.history[chistory.index], now)) {
push(history, now, true)
export function undo<T>(history: History<T> | undefined, now: T): T {
if (history) {
let chistory = get(history)
if (chistory.index == 0) return now
if (!deepEqual(chistory.history[chistory.index], now)) {
push(history, now, true)
} else {
history.update((history) => {
history.index--
return history
})
}
let nhistory = get(history)
return nhistory.history[nhistory.index]
} else {
history.update((history) => {
history.index--
return now
}
}
export function redo<T>(history: History<T> | undefined): T {
if (history) {
history?.update((history) => {
if (history.index < history.history.length - 1) {
history.index++
}
return history
})
let nhistory = get(history)
return nhistory.history[nhistory.index]
} else {
return undefined as T
}
let nhistory = get(history)
return nhistory.history[nhistory.index]
}
export function redo<T>(history: History<T>): T {
history.update((history) => {
if (history.index < history.history.length - 1) {
history.index++
}
return history
})
let nhistory = get(history)
return nhistory.history[nhistory.index]
}
export function push<T>(history: History<T>, value: T, noSetIndex: boolean = false, skipCopy: boolean = false) {
history.update((history) => {
export function push<T>(history: History<T> | undefined, value: T, noSetIndex: boolean = false, skipCopy: boolean = false) {
history?.update((history) => {
history.history = JSON.parse(JSON.stringify(history.history.slice(0, history.index + 1)))
const toPush = skipCopy ? value : JSON.parse(JSON.stringify(value))
history.history.push(toPush)
+1
View File
@@ -114,6 +114,7 @@ export async function main(
export const BASH_INIT_CODE = `# arguments of the form X="$I" are parsed as parameters X of type string
msg="$1"
dflt="\${2:-default value}"
# the last line of the stdout is the return value
echo "Hello $msg"