Skip to content

Commit

Permalink
Refactor widgets to handle dynamic config in input (#8624)
Browse files Browse the repository at this point in the history
* Merged ArgumentAst and ArgumentPlaceholder into single class.
* Created `AnyWidget` input being a "general use" widget input. Most wigets try to match with it; the `Argument` input is now solely for WidgetArgument(Name) or those handling arguments in a specific way (like selector which want to show on arg name click).
* dynamic config is now part of widget input, and is properly propagated through vector editor/function widgets.

# Important Notes
The widgets still does not work perfectly:
* The chosen options often don't have argument placeholders - that's because we don't display them for constructors. Needs to be added on our side, or engine should send us methodCall info for constructors.
* There are issues with engine's messages sent to us. This makes widgets does not set up (so there is no drop-down, or vector adds `_` instead of default). I'm investigating them and going to fill issues.
  • Loading branch information
farmaazon authored Jan 3, 2024
1 parent cfab344 commit 2e7d71d
Show file tree
Hide file tree
Showing 30 changed files with 684 additions and 447 deletions.
2 changes: 1 addition & 1 deletion app/gui2/shared/languageServerTypes/suggestions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface SuggestionEntryArgument {
/** The argument name. */
name: string
/** The argument type. String 'Any' is used to specify generic types. */
type: string
reprType: string
/** Indicates whether the argument is lazy. */
isSuspended: boolean
/** Indicates whether the argument has default value. */
Expand Down
2 changes: 1 addition & 1 deletion app/gui2/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { computed, onMounted } from 'vue'
const props = defineProps<{
config: ApplicationConfig
accessToken: string
accessToken: string | null
metadata: object
unrecognizedOptions: string[]
}>()
Expand Down
29 changes: 8 additions & 21 deletions app/gui2/src/components/GraphEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import * as set from 'lib0/set'
import type { ExprId, NodeMetadata } from 'shared/yjsModel'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { toast } from 'vue3-toastify'
import { computed, onMounted, onScopeDispose, ref, watch } from 'vue'
import { toast, type Id as ToastId } from 'vue3-toastify'
import { type Usage } from './ComponentBrowser/input'
const STARTUP_TOAST_DELAY_MS = 100
const EXECUTION_MODES = ['design', 'live']
// Assumed size of a newly created node. This is used to place the component browser.
const DEFAULT_NODE_SIZE = new Vec2(0, 24)
Expand All @@ -52,19 +53,13 @@ const suggestionDb = useSuggestionDbStore()
const interaction = provideInteractionHandler()
function initStartupToast() {
const startupToast = toast.info('Initializing the project. This can take up to one minute.', {
let startupToast = toast.info('Initializing the project. This can take up to one minute.', {
autoClose: false,
})
projectStore.firstExecution.then(() => {
if (startupToast != null) {
toast.remove(startupToast)
}
})
onUnmounted(() => {
if (startupToast != null) {
toast.remove(startupToast)
}
})
const removeToast = () => toast.remove(startupToast)
projectStore.firstExecution.then(removeToast)
onScopeDispose(removeToast)
}
onMounted(() => {
Expand Down Expand Up @@ -548,14 +543,6 @@ function handleEdgeDrop(source: ExprId, position: Vec2) {
@dragover.prevent
@drop.prevent="handleFileDrop($event)"
>
<ToastContainer
position="top-center"
theme="light"
closeOnClick="false"
draggable="false"
toastClassName="text-sm leading-170 bg-frame-selected rounded-2xl backdrop-blur-3xl"
transition="Vue-Toastification__bounce"
/>
<div :style="{ transform: graphNavigator.transform }" class="htmlLayer">
<GraphNodes
@nodeOutputPortDoubleClick="handleNodeOutputPortDoubleClick"
Expand Down
4 changes: 0 additions & 4 deletions app/gui2/src/components/GraphEditor/NodeWidget.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
import type { PortId } from '@/providers/portInfo'
import { injectWidgetRegistry, type WidgetInput } from '@/providers/widgetRegistry'
import type { WidgetConfiguration } from '@/providers/widgetRegistry/configuration'
import { injectWidgetTree } from '@/providers/widgetTree'
import {
injectWidgetUsageInfo,
Expand All @@ -14,7 +13,6 @@ import { computed, proxyRefs } from 'vue'
const props = defineProps<{
input: WidgetInput
nest?: boolean
dynamicConfig?: WidgetConfiguration | undefined
/**
* A function that intercepts and handles a value update emitted by this widget. When it returns
* `false`, the update continues to be propagated to the parent widget. When it returns `true`,
Expand Down Expand Up @@ -43,7 +41,6 @@ const selectedWidget = computed(() => {
return registry.select(
{
input: props.input,
config: props.dynamicConfig ?? undefined,
nesting: nesting.value,
},
sameInputParentWidgets.value,
Expand Down Expand Up @@ -95,7 +92,6 @@ const spanStart = computed(() => {
v-if="selectedWidget"
ref="rootNode"
:input="props.input"
:config="dynamicConfig"
:nesting="nesting"
:data-span-start="spanStart"
@update="updateHandler"
Expand Down
6 changes: 4 additions & 2 deletions app/gui2/src/components/GraphEditor/NodeWidgetTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import NodeWidget from '@/components/GraphEditor/NodeWidget.vue'
import { useTransitioning } from '@/composables/animation'
import { ForcePort, type PortId } from '@/providers/portInfo'
import { AnyWidget } from '@/providers/widgetRegistry'
import { provideWidgetTree } from '@/providers/widgetTree'
import { useGraphStore } from '@/stores/graph'
import { Ast } from '@/util/ast'
Expand All @@ -11,9 +12,10 @@ import { computed, toRef } from 'vue'
const props = defineProps<{ ast: Ast.Ast }>()
const graph = useGraphStore()
const rootPort = computed(() => {
const input = AnyWidget.Ast(props.ast)
return props.ast instanceof Ast.Ident && !graph.db.isKnownFunctionCall(props.ast.exprId)
? new ForcePort(props.ast)
: props.ast
? new ForcePort(input)
: input
})
const observedLayoutTransitions = new Set([
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
<script setup lang="ts">
import NodeWidget from '@/components/GraphEditor/NodeWidget.vue'
import { ForcePort } from '@/providers/portInfo'
import { defineWidget, widgetProps } from '@/providers/widgetRegistry'
import { AnyWidget, defineWidget, widgetProps } from '@/providers/widgetRegistry'
import { Ast } from '@/util/ast'
import { ArgumentApplication } from '@/util/callTree'
import { ArgumentApplication, ArgumentAst, ArgumentPlaceholder } from '@/util/callTree'
import { computed } from 'vue'
const props = defineProps(widgetProps(widgetDefinition))
const targetMaybePort = computed(() =>
props.input.target instanceof Ast.Ast ? new ForcePort(props.input.target) : props.input.target,
props.input.target instanceof ArgumentPlaceholder || props.input.target instanceof ArgumentAst
? new ForcePort(props.input.target.toAnyWidget())
: props.input.target instanceof Ast.Ast
? AnyWidget.Ast(props.input.target)
: props.input.target,
)
const appClass = computed(() => {
Expand Down Expand Up @@ -39,7 +43,7 @@ export const widgetDefinition = defineWidget(ArgumentApplication, {
<div v-if="props.input.infixOperator" class="infixOp" :style="operatorStyle">
<NodeWidget :input="props.input.infixOperator" />
</div>
<NodeWidget :input="props.input.argument" :dynamicConfig="props.config" />
<NodeWidget :input="props.input.argument" />
</span>
</template>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ export const widgetDefinition = defineWidget(ArgumentAst, {
</script>

<template>
<NodeWidget :input="props.input.ast" nest />
<NodeWidget :input="props.input.toAnyWidget()" nest />
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,23 @@ const primary = computed(() => props.nesting < 2)
</script>

<script lang="ts">
export const widgetDefinition = defineWidget([ArgumentPlaceholder, ArgumentAst], {
export const widgetDefinition = defineWidget([ArgumentAst.matchWithArgInfo, ArgumentPlaceholder], {
priority: 1000,
score: (props) =>
props.input.info != null &&
(props.input instanceof ArgumentPlaceholder ||
(props.nesting < 2 && props.input.kind === ApplicationKind.Prefix))
? Score.Perfect
: Score.Mismatch,
score: (props) => {
const isPlaceholder = props.input instanceof ArgumentPlaceholder
const isTopArg = props.nesting < 2 && props.input.kind === ApplicationKind.Prefix
return isPlaceholder || isTopArg ? Score.Perfect : Score.Mismatch
},
})
</script>

<template>
<div class="WidgetArgumentName" :class="{ placeholder, primary }">
<template v-if="showArgumentValue">
<span class="value">{{ props.input.info!.name }}</span
<span class="value">{{ props.input.argInfo.name }}</span
><NodeWidget :input="props.input" />
</template>
<template v-else>{{ props.input.info!.name }}</template>
<template v-else>{{ props.input.argInfo.name }}</template>
</div>
</template>

Expand Down
13 changes: 8 additions & 5 deletions app/gui2/src/components/GraphEditor/widgets/WidgetBlank.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<script setup lang="ts">
import { Score, defineWidget, widgetProps } from '@/providers/widgetRegistry'
import { AnyWidget, Score, defineWidget, widgetProps } from '@/providers/widgetRegistry'
import { Ast } from '@/util/ast'
const _props = defineProps(widgetProps(widgetDefinition))
</script>

<script lang="ts">
export const widgetDefinition = defineWidget(Ast.Wildcard, {
priority: 10,
score: Score.Good,
})
export const widgetDefinition = defineWidget(
(input) => input instanceof AnyWidget && input.ast instanceof Ast.Wildcard,
{
priority: 10,
score: Score.Good,
},
)
</script>

<template>
Expand Down
26 changes: 12 additions & 14 deletions app/gui2/src/components/GraphEditor/widgets/WidgetCheckbox.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
<script setup lang="ts">
import CheckboxWidget from '@/components/widgets/CheckboxWidget.vue'
import { Score, defineWidget, widgetProps } from '@/providers/widgetRegistry'
import { AnyWidget, Score, defineWidget, widgetProps } from '@/providers/widgetRegistry'
import { Ast } from '@/util/ast'
import { computed } from 'vue'
const props = defineProps(widgetProps(widgetDefinition))
const value = computed({
get() {
return props.input.code().endsWith('True') ?? false
return props.input.ast?.code().endsWith('True') ?? false
},
set(value) {
const node = getRawBoolNode(props.input)
if (props.input.ast == null) return // TODO[ao] set value on placeholder here.
const node = getRawBoolNode(props.input.ast)
if (node != null) {
props.onUpdate(value ? 'True' : 'False', node.exprId)
}
Expand All @@ -29,18 +30,15 @@ function getRawBoolNode(ast: Ast.Ast) {
return null
}
export const widgetDefinition = defineWidget(
(input) => input instanceof Ast.PropertyAccess || input instanceof Ast.Ident,
{
priority: 10,
score: (props) => {
if (getRawBoolNode(props.input) != null) {
return Score.Perfect
}
return Score.Mismatch
},
export const widgetDefinition = defineWidget(AnyWidget, {
priority: 10,
score: (props) => {
if (props.input.ast == null)
return props.input.argInfo?.reprType === 'Standard.Base.Bool' ? Score.Good : Score.Mismatch
if (getRawBoolNode(props.input.ast) != null) return Score.Perfect
return Score.Mismatch
},
)
})
</script>

<template>
Expand Down
Loading

0 comments on commit 2e7d71d

Please sign in to comment.