mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 16:02:24 +00:00
feat: full-code app builder reachable from home in preview
This commit is contained in:
@@ -1,17 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { createBubbler, stopPropagation } from 'svelte/legacy'
|
||||
|
||||
const bubble = createBubbler()
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
import Button from '../button/Button.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import CloseButton from '../CloseButton.svelte'
|
||||
|
||||
export let title: string
|
||||
export let open: boolean = false
|
||||
let c: string = ''
|
||||
export { c as class }
|
||||
export let style = ''
|
||||
export let cancelText: string | undefined = undefined
|
||||
export let kind: 'button' | 'X' = 'button'
|
||||
interface Props {
|
||||
title: string
|
||||
open?: boolean
|
||||
class?: string
|
||||
style?: string
|
||||
cancelText?: string | undefined
|
||||
kind?: 'button' | 'X'
|
||||
settings?: import('svelte').Snippet
|
||||
children?: import('svelte').Snippet
|
||||
actions?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let {
|
||||
title,
|
||||
open = $bindable(false),
|
||||
class: c = '',
|
||||
style = '',
|
||||
cancelText = undefined,
|
||||
kind = 'button',
|
||||
settings,
|
||||
children,
|
||||
actions
|
||||
}: Props = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -37,13 +56,13 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown|capture={onKeyDown} />
|
||||
<svelte:window onkeydowncapture={onKeyDown} />
|
||||
|
||||
{#if open}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
||||
<div
|
||||
on:click={() => (open = false)}
|
||||
onclick={() => (open = false)}
|
||||
transition:fadeFast|local
|
||||
class={'fixed top-0 bottom-0 left-0 right-0 z-[9999]'}
|
||||
role="dialog"
|
||||
@@ -58,9 +77,9 @@
|
||||
|
||||
<div class="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div class="flex min-h-full items-center justify-center p-4">
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
on:click|stopPropagation
|
||||
onclick={stopPropagation(bubble('click'))}
|
||||
class={twMerge(
|
||||
'relative transform overflow-hidden rounded-lg bg-surface px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6',
|
||||
c,
|
||||
@@ -78,17 +97,17 @@
|
||||
<div class="ml-4 text-left flex-1">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<h3>{title}</h3>
|
||||
<slot name="settings" />
|
||||
{@render settings?.()}
|
||||
</div>
|
||||
|
||||
<div class="mt-4 text-sm text-primary">
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if kind == 'button'}
|
||||
<div class="flex items-center space-x-2 flex-row-reverse space-x-reverse mt-4">
|
||||
<slot name="actions" />
|
||||
{@render actions?.()}
|
||||
<Button
|
||||
on:click={() => {
|
||||
dispatch('canceled')
|
||||
|
||||
@@ -5,21 +5,47 @@
|
||||
import { Button } from '$lib/components/common'
|
||||
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
|
||||
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
|
||||
import { LayoutDashboard, Loader2, Plus } from 'lucide-svelte'
|
||||
import Modal from '$lib/components/common/modal/Modal.svelte'
|
||||
import { LayoutDashboard, Loader2, Plus, Code2, FlaskConical } from 'lucide-svelte'
|
||||
import { importStore } from '../apps/store'
|
||||
import Badge from '$lib/components/common/badge/Badge.svelte'
|
||||
|
||||
import YAML from 'yaml'
|
||||
import Tooltip from '../Tooltip.svelte'
|
||||
|
||||
let drawer: Drawer | undefined = undefined
|
||||
let pendingRaw: string = ''
|
||||
|
||||
let importType: 'yaml' | 'json' = 'yaml'
|
||||
|
||||
// Modal states
|
||||
let appTypeModalOpen = $state(false)
|
||||
let featurePreviewModalOpen = $state(false)
|
||||
|
||||
async function importRaw() {
|
||||
$importStore = importType === 'yaml' ? YAML.parse(pendingRaw) : JSON.parse(pendingRaw)
|
||||
await goto('/apps/add?nodraft=true')
|
||||
drawer?.closeDrawer?.()
|
||||
}
|
||||
|
||||
function openAppTypeModal() {
|
||||
appTypeModalOpen = true
|
||||
}
|
||||
|
||||
function selectLowCode() {
|
||||
appTypeModalOpen = false
|
||||
goto(`${base}/apps/add?nodraft=true`)
|
||||
}
|
||||
|
||||
function selectFullCode() {
|
||||
appTypeModalOpen = false
|
||||
featurePreviewModalOpen = true
|
||||
}
|
||||
|
||||
function confirmFullCode() {
|
||||
featurePreviewModalOpen = false
|
||||
goto(`${base}/apps_raw/add?nodraft=true`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Buttons -->
|
||||
@@ -27,11 +53,11 @@
|
||||
<Button
|
||||
id="create-app-button"
|
||||
aiId="apps-create-actions-app"
|
||||
aiDescription="Create a new low-code app"
|
||||
aiDescription="Create a new app"
|
||||
unifiedSize="lg"
|
||||
startIcon={{ icon: Plus }}
|
||||
endIcon={{ icon: LayoutDashboard }}
|
||||
href="{base}/apps/add?nodraft=true"
|
||||
on:click={openAppTypeModal}
|
||||
variant="accent"
|
||||
dropdownItems={[
|
||||
{
|
||||
@@ -48,16 +74,101 @@
|
||||
importType = 'json'
|
||||
}
|
||||
}
|
||||
// {
|
||||
// label: 'Build app in React/Vue/Svelte (alpha)',
|
||||
// onClick: () => goto('/apps_raw/add?nodraft=true')
|
||||
// }
|
||||
]}
|
||||
>
|
||||
<div class="flex flex-row items-center"> App </div>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- App Type Selection Modal -->
|
||||
<Modal bind:open={appTypeModalOpen} title="Choose your app builder">
|
||||
<div class="flex flex-col gap-4 pr-4">
|
||||
<div class="grid grid-cols-2 gap-8">
|
||||
<!-- Low-code option -->
|
||||
<button
|
||||
class="flex flex-col items-center gap-3 p-6 rounded-lg border-2 border-gray-200 dark:border-gray-700 hover:border-orange-500 dark:hover:border-orange-400 hover:bg-orange-50 dark:hover:bg-orange-900/20 transition-all cursor-pointer group"
|
||||
onclick={selectLowCode}
|
||||
>
|
||||
<div
|
||||
class="w-32 h-32 rounded-xl bg-orange-100 dark:bg-orange-900/40 flex items-center justify-center group-hover:bg-orange-200 dark:group-hover:bg-orange-800/50 transition-colors"
|
||||
>
|
||||
<LayoutDashboard size={32} class="text-orange-600 dark:text-orange-400" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h3 class="font-semibold text-primary">Low-code App</h3>
|
||||
<p class="text-xs text-tertiary mt-1">
|
||||
Drag-and-drop UI builder with 60+ powerful components.
|
||||
|
||||
<br /><br />
|
||||
Better for simple apps or apps that require minimal customization.
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="relative flex flex-col items-center gap-3 p-6 rounded-lg border-2 border-gray-200 dark:border-gray-700 hover:border-purple-500 dark:hover:border-purple-400 hover:bg-purple-50 dark:hover:bg-purple-900/20 transition-all cursor-pointer group"
|
||||
onclick={selectFullCode}
|
||||
>
|
||||
<div
|
||||
class="w-32 h-32 rounded-xl bg-purple-100 dark:bg-purple-900/40 flex items-center justify-center group-hover:bg-purple-200 dark:group-hover:bg-purple-800/50 transition-colors"
|
||||
>
|
||||
<Code2 size={32} class="text-purple-600 dark:text-purple-400" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h3 class="font-semibold text-primary">Full-code App</h3>
|
||||
<p class="text-xs text-tertiary mt-1">
|
||||
Build with React or Svelte with full control and a powerful AI agent.
|
||||
</p>
|
||||
</div>
|
||||
<br /><br />
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<Badge color="yellow">
|
||||
<FlaskConical size={12} class="mr-1" />
|
||||
Preview (unstable)
|
||||
</Badge>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!-- Feature Preview Warning Modal -->
|
||||
<Modal kind="X" bind:open={featurePreviewModalOpen} title="Feature Preview Notice">
|
||||
<div class="flex flex-col gap-4">
|
||||
<div
|
||||
class="flex items-start gap-3 p-4 rounded-lg bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800"
|
||||
>
|
||||
<FlaskConical size={24} class="text-yellow-600 dark:text-yellow-400 shrink-0 mt-0.5" />
|
||||
<div class="text-sm text-yellow-800 dark:text-yellow-200">
|
||||
<p class="font-semibold mb-2">Full-code Apps are in Feature Preview</p>
|
||||
<p>
|
||||
This feature is not yet stable and may change significantly before it reaches General
|
||||
Availability (GA), which is expected in <strong>January 2026</strong>.
|
||||
</p>
|
||||
<ul class="list-disc list-inside mt-2 space-y-1 text-yellow-700 dark:text-yellow-300">
|
||||
<li>APIs and interfaces may change without notice</li>
|
||||
<li>Some functionality may be incomplete or buggy</li>
|
||||
<li>Not recommended for production use yet</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-3 pt-2">
|
||||
<Button
|
||||
color="light"
|
||||
size="sm"
|
||||
on:click={() => {
|
||||
featurePreviewModalOpen = false
|
||||
appTypeModalOpen = true
|
||||
}}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
<Button color="blue" size="sm" on:click={confirmFullCode}>I understand, continue</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!-- Raw JSON -->
|
||||
<Drawer bind:this={drawer} size="800px">
|
||||
<DrawerContent
|
||||
|
||||
@@ -24,7 +24,15 @@
|
||||
import Select from '$lib/components/select/Select.svelte'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { AlertTriangle, Sparkles, ArrowRight, Plus, List, Ban } from 'lucide-svelte'
|
||||
import {
|
||||
AlertTriangle,
|
||||
Sparkles,
|
||||
ArrowRight,
|
||||
Plus,
|
||||
List,
|
||||
Ban,
|
||||
ExternalLinkIcon
|
||||
} from 'lucide-svelte'
|
||||
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
|
||||
import RawAppDataTableList from '$lib/components/raw_apps/RawAppDataTableList.svelte'
|
||||
@@ -398,8 +406,24 @@
|
||||
<AlertTriangle size={16} class="text-yellow-600 dark:text-yellow-400 shrink-0" />
|
||||
<div class="text-sm text-yellow-800 dark:text-yellow-200">
|
||||
<span class="font-medium">No datatables configured.</span>
|
||||
You can still create an app, but AI won't be able to create database tables. Configure
|
||||
datatables in workspace settings to enable this feature.
|
||||
You can still create an app, but for data storage you won't be able to use data tables
|
||||
which are <b>HIGHLY RECOMMENDED</b>.
|
||||
<br />
|
||||
|
||||
{#if $userStore?.is_admin}
|
||||
Configure datatables in
|
||||
<a
|
||||
href="/workspace/settings?tab=windmill_data_tables"
|
||||
target="_blank"
|
||||
class="inline-flex items-center gap-1"
|
||||
>workspace settings <ExternalLinkIcon size={16} />
|
||||
</a> to enable this feature.
|
||||
{:else}
|
||||
<b>
|
||||
Ask your workspace admin to configure datatables in workspace settings to enable
|
||||
this feature.
|
||||
</b>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
@@ -509,7 +533,23 @@
|
||||
>
|
||||
<AlertTriangle size={16} class="text-gray-500 shrink-0" />
|
||||
<div class="text-sm text-tertiary">
|
||||
AI is not configured for this workspace. You can still create an app manually.
|
||||
AI is not configured for this workspace. You can still create an app manually but <b
|
||||
>using AI is highly recommended</b
|
||||
>.
|
||||
<br />
|
||||
{#if $userStore?.is_admin}
|
||||
Configure AI in
|
||||
<a
|
||||
href="/workspace/settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex items-center gap-1"
|
||||
>workspace settings <ExternalLinkIcon size={16} />
|
||||
</a>
|
||||
to enable this feature.
|
||||
{:else}
|
||||
Ask your workspace admin to configure AI in workspace settings to enable this
|
||||
feature.
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
|
||||
Reference in New Issue
Block a user