feat(frontend): Typography update (#725)

* feat(frontend): Add new font face

* feat(frontend): Typography update

* fix(frontend): Import Inter font

* fix(frontend): Font fixes

* fix(front): Fix spacing and font inconsistencies

* fix(frontend): Visual changes and fixes

* fix(frontend): Minor visual fixes

* fix(frontend): Remove comment
This commit is contained in:
Ádám Kovács
2022-10-13 14:04:26 +02:00
committed by GitHub
parent 444750d594
commit 2c1cd7eea8
43 changed files with 561 additions and 464 deletions
+9
View File
@@ -2,3 +2,12 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
@font-face {
font-family: 'Inter';
src: url('/Inter-Variable.ttf');
font-weight: 100 900;
font-display: swap;
}
}
+9 -10
View File
@@ -20,10 +20,7 @@
import { oauthStore, userStore, workspaceStore } from '$lib/stores'
import { faMinus, faPlus } from '@fortawesome/free-solid-svg-icons'
import IconedResourceType from './IconedResourceType.svelte'
import PageHeader from './PageHeader.svelte'
import { OauthService, ResourceService, VariableService, type TokenResponse } from '$lib/gen'
import { page } from '$app/stores'
import { sendUserToast, truncateRev } from '$lib/utils'
import { createEventDispatcher } from 'svelte'
@@ -209,10 +206,11 @@
</p>
</div>
{/if}
<PageHeader title="OAuth APIs" />
<div class="mb-1 font-semibold text-gray-700">OAuth APIs</div>
<div class="grid sm:grid-cols-2 md:grid-cols-3 gap-x-2 gap-y-1 items-center mb-2">
{#each Object.entries(connects).sort((a, b) => a[0].localeCompare(b[0])) as [key, values]}
<Button
size="sm"
variant="border"
color={key === resource_type ? 'blue' : 'dark'}
btnClasses={key === resource_type ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
@@ -225,11 +223,11 @@
dispatch('click')
}}
>
<IconedResourceType name={key} after={true} />
<IconedResourceType name={key} after={true} width="20px" height="20px" />
</Button>
{/each}
</div>
<PageHeader title="Scopes" primary={false} />
<div class="mb-1 font-semibold text-gray-700 mt-6">Scopes</div>
{#if !manual && resource_type != ''}
{#each scopes as v}
<div class="flex flex-row max-w-md mb-2">
@@ -266,7 +264,7 @@
{:else}
<p class="italic text-sm">Pick an OAuth API and customize the scopes here</p>
{/if}
<PageHeader title="Extra Params" primary={false} />
<div class="mb-1 font-semibold text-gray-700 mt-6">Extra params</div>
{#if !manual && resource_type != ''}
{#each extra_params as [k, v]}
<div class="flex flex-row max-w-md mb-2">
@@ -306,10 +304,11 @@
{:else}
<p class="italic text-sm">Pick an OAuth API and customize the extra parameters here</p>
{/if}
<PageHeader title="Non OAuth APIs" />
<div class="mb-1 font-semibold text-gray-700 mt-6">Non OAuth APIs</div>
<div class="grid sm:grid-cols-2 md:grid-cols-3 gap-x-2 gap-y-1 items-center mb-2">
{#each connectsManual as [key, instructions]}
<Button
size="sm"
variant="border"
color={key === resource_type ? 'blue' : 'dark'}
btnClasses={key === resource_type ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
@@ -319,13 +318,13 @@
dispatch('click')
}}
>
<IconedResourceType name={key} after={true} />
<IconedResourceType name={key} after={true} width="20px" height="20px" />
</Button>
{/each}
</div>
{:else if step == 2}
{#if manual}
<PageHeader title="Instructions" />
<div class="mb-1 font-semibold text-gray-700 mt-6">Instructions</div>
<div>
{apiTokenApps[resource_type].instructions}
</div>
+6 -6
View File
@@ -196,7 +196,7 @@
<div>
<Button
color="light"
btnClasses="mr-1"
btnClasses="mr-1 !font-medium"
on:click={contextualVariablePicker.openModal}
size="xs"
spacingSize="md"
@@ -209,7 +209,7 @@
<div>
<Button
color="light"
btnClasses="mr-1"
btnClasses="mr-1 !font-medium"
on:click={variablePicker.openModal}
size="xs"
spacingSize="md"
@@ -221,7 +221,7 @@
</div>
<div>
<Button
btnClasses="mx-1"
btnClasses="mx-1 !font-medium"
size="xs"
spacingSize="md"
color="light"
@@ -235,7 +235,7 @@
<div>
<Button
btnClasses="mx-1"
btnClasses="mx-1 !font-medium"
size="xs"
spacingSize="md"
color="light"
@@ -249,7 +249,7 @@
<div>
<Button
btnClasses="mx-1"
btnClasses="mx-1 !font-medium"
size="xs"
spacingSize="md"
color="light"
@@ -263,7 +263,7 @@
</div>
<div>
<Button
btnClasses="ml-1"
btnClasses="ml-1 !font-medium"
size="xs"
spacingSize="md"
color="light"
@@ -11,15 +11,16 @@
| undefined = undefined
</script>
<h3>
<div>
<span class="font-semibold">
{label}<Required {required} />
{label}
</span>
<span class="text-sm italic text-indigo-800"
>({type ?? 'any'}{contentEncoding && contentEncoding != ''
<span class="text-sm italic text-indigo-800">
({type ?? 'any'}{contentEncoding && contentEncoding != ''
? `, encoding: ${contentEncoding}`
: ''}{format && format != '' ? `, format: ${format}` : ''}{itemsType?.type
? ` of ${itemsType?.type}s`
: ''})</span
>
</h3>
<Required {required} class="!ml-0" />
</div>
@@ -15,7 +15,7 @@
import Icon from 'svelte-awesome'
import { writable } from 'svelte/store'
import CenteredPage from './CenteredPage.svelte'
import Button from './common/button/Button.svelte'
import { Button } from './common'
import { dirtyStore } from './common/confirmationModal/dirtyStore'
import UnsavedConfirmationModal from './common/confirmationModal/UnsavedConfirmationModal.svelte'
import { OFFSET } from './CronInput.svelte'
+41 -35
View File
@@ -54,13 +54,19 @@
</script>
<Modal bind:this={modal}>
<div slot="title">group {name}</div>
<div slot="content">
<PageHeader title="Summary" />
<p>{group?.summary ?? 'No summary'}</p>
<PageHeader title="Members">
<div slot="title">
{name}
<span class="text-sm text-gray-500 ml-1">(group)</span>
</div>
<div slot="content" class="flex flex-col gap-6">
<div>
<div class="font-semibold text-gray-700 mb-1">Summary</div>
<p>{group?.summary ?? 'No summary'}</p>
</div>
<div>
<div class="font-semibold text-gray-700 mb-1">Members</div>
{#if can_write}
<div>
<div class="flex items-start">
<AutoComplete items={usernames} bind:selectedItem={username} />
<Button
variant="contained"
@@ -73,34 +79,34 @@
</Button>
</div>
{/if}
</PageHeader>
<TableCustom>
<tr slot="header-row">
<th>user</th>
<th>admin of group</th>
<th />
</tr>
<tbody slot="body">
{#each members as { name, isAdmin }}<tr>
<td>{name}</td>
<td> {isAdmin ? 'admin' : ''} </td>
<td>
{#if can_write}
<button
class="ml-2 text-red-500"
on:click={async () => {
await GroupService.removeUserToGroup({
workspace: $workspaceStore ?? '',
name: group?.name ?? '',
requestBody: { username: name }
})
loadGroup()
}}>remove</button
>
{/if}</td
>
</tr>{/each}
</tbody></TableCustom
>
<TableCustom>
<tr slot="header-row">
<th>user</th>
<th>admin of group</th>
<th />
</tr>
<tbody slot="body">
{#each members as { name, isAdmin }}<tr>
<td>{name}</td>
<td> {isAdmin ? 'admin' : ''} </td>
<td>
{#if can_write}
<button
class="ml-2 text-red-500"
on:click={async () => {
await GroupService.removeUserToGroup({
workspace: $workspaceStore ?? '',
name: group?.name ?? '',
requestBody: { username: name }
})
loadGroup()
}}>remove</button
>
{/if}</td
>
</tr>{/each}
</tbody>
</TableCustom>
</div>
</div>
</Modal>
@@ -28,7 +28,7 @@
export let width = '24px'
</script>
<div class="flex flex-row gap-2 justify-center -pl-2 whitespace-nowrap">
<div class="flex flex-row gap-2 justify-center items-center break-all whitespace-normal -pl-2">
{#if !silent && !after}
{name}
{/if}
@@ -44,7 +44,7 @@
{:else if name === 'slack'}
<Slack {height} {width} />
{:else if name === 'github'}
<GithubIcon />
<GithubIcon {height} {width} />
{:else if name === 'gmail'}
<GmailIcon {height} {width} />
{:else if name === 'gsheets'}
@@ -6,17 +6,17 @@
export let primary: boolean = true
</script>
<div class="flex flex-col sm:flex-row justify-between mt-4 mb-2">
<div class="flex flex-col sm:flex-row justify-between mt-4 mb-2 min-h-[48px]">
{#if primary}
<span class="text-xl font-bold flex items-center space-x-2">
<span class="mr-0.5">{title}</span>
<span class="flex items-center space-x-2">
<h1 class="mr-0.5">{title}</h1>
{#if tooltip}
<Tooltip>{tooltip}</Tooltip>
{/if}
</span>
{:else}
<span class="text-lg font-semibold flex items-center space-x-2">
<span class="mr-0.5">{title}</span>
{:else}
<span class="flex items-center space-x-2">
<h2 class="mr-0.5">{title}</h2>
{#if tooltip}
<Tooltip>{tooltip}</Tooltip>
{/if}
+1 -1
View File
@@ -28,7 +28,7 @@
})
</script>
<label class="block text-gray-700" for="password"> {label} </label>
<label class="block text-gray-700" for="password"> {@html label} </label>
<div class="relative w-full">
<div class="absolute inset-y-0 right-0 flex items-center px-2">
<input class="hidden js-password-toggle" id="toggle" type="checkbox" />
+7 -3
View File
@@ -14,6 +14,7 @@
import { userStore, workspaceStore } from '$lib/stores'
import { sleep } from '$lib/utils'
import { createEventDispatcher } from 'svelte'
import Required from './Required.svelte'
type PathKind = 'resource' | 'script' | 'variable' | 'flow' | 'schedule'
export let meta: Meta = {
@@ -175,7 +176,7 @@
</label>
{#if meta.ownerKind === 'user'}
<label class="block">
<span class="text-sm text-gray-700">Owner</span>
<span class="text-gray-700 text-sm">Owner</span>
<input
bind:value={meta.owner}
placeholder={$userStore?.username ?? ''}
@@ -184,7 +185,7 @@
</label>
{:else}
<label class="block">
<span class="text-sm text-gray-700">Owner</span>
<span class="text-gray-700 text-sm">Owner</span>
<select bind:value={meta.owner}>
{#each groups as g}
<option>{g.name}</option>
@@ -193,7 +194,10 @@
</label>
{/if}
<label class="block col-span-2">
<span class="text-gray-700 text-sm">Name<span class="text-red-600 text-sm">*</span></span>
<span class="text-gray-700 text-sm">
Name
<Required required={true} />
</span>
<input
autofocus
autocomplete="off"
+2 -2
View File
@@ -4,9 +4,9 @@
</script>
{#if required}
<span class="text-red-600 font-normal">*</span>
<span class="text-red-700 font-normal {$$props.class}">*</span>
{:else}
<span class="text-sm text-gray-500 ml-2 font-normal"
<span class="text-sm text-gray-500 ml-2 font-normal {$$props.class}"
>(optional{detail != '' ? `, ${detail}` : ''})</span
>
{/if}
@@ -33,6 +33,7 @@
{#if nonePickable}
{@const isPicked = value === undefined}
<Button
size="sm"
variant="border"
color={isPicked ? 'blue' : 'dark'}
btnClasses={isPicked ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
@@ -45,13 +46,14 @@
{#each resources as r}
{@const isPicked = value === r}
<Button
size="sm"
variant="border"
color={isPicked ? 'blue' : 'dark'}
btnClasses={isPicked ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
disabled={notPickable}
on:click={() => onClick(r)}
>
<IconedResourceType name={r} after={true} />
<IconedResourceType name={r} after={true} width="20px" height="20px" />
</Button>
{/each}
</div>
@@ -143,7 +143,7 @@
<Button
variant="contained"
color="blue"
size="md"
size="sm"
startIcon={{ icon: faPlus }}
on:click={() => {
modalProperty = Object.assign({}, DEFAULT_PROPERTY)
+76 -70
View File
@@ -98,67 +98,72 @@
<Modal bind:this={modal} on:close={clearModal}>
<div slot="title">Add an argument</div>
<div slot="content">
<div class="flex flex-col px-6 py-3 bg-gray-50">
<label class="font-semibold text-gray-700"
>Name<Required required={true} />
<input
autofocus
autocomplete="off"
type="text"
placeholder="name"
bind:value={property.name}
class={error === ''
? ''
: 'border border-red-700 bg-red-100 border-opacity-30 focus:border-red-700 focus:border-opacity-30 focus-visible:ring-red-700 focus-visible:ring-opacity-25 focus-visible:border-red-700'}
/>
</label>
<div class="mb-2 text-red-600 text-2xs">{error}</div>
<div class="flex flex-col gap-6">
<div>
<label class="block">
<div class="mb-1 font-semibold text-gray-700">
Name
<Required required={true} />
</div>
<input
autofocus
autocomplete="off"
type="text"
placeholder="Enter a name"
bind:value={property.name}
class={error === ''
? ''
: 'border border-red-700 bg-red-100 border-opacity-30 focus:border-red-700 focus:border-opacity-30 focus-visible:ring-red-700 focus-visible:ring-opacity-25 focus-visible:border-red-700'}
/>
</label>
<div class="text-red-600 text-2xs">{error}</div>
</div>
<label class="mb-2 font-semibold text-gray-700">
Description
<textarea
class="mb-1"
type="text"
placeholder="Type message..."
rows="3"
bind:value={property.description}
/>
<label class="block">
<div class="mb-1 font-semibold text-gray-700">
Description
<Required required={false} />
</div>
<textarea placeholder="Enter a description" rows="3" bind:value={property.description} />
</label>
<h3 class="font-semibold text-gray-700">Type<Required required={true} /></h3>
<div class="grid sm:grid-cols-3 md:grid-cols-4 gap-x-2 gap-y-1 items-center mb-2 w-full">
{#each ARG_TYPES as argType}
{@const isSelected = argType == property.selectedType}
<div>
<div class="mb-1 font-semibold text-gray-700">Type<Required required={true} /></div>
<div class="grid sm:grid-cols-3 md:grid-cols-4 gap-x-2 gap-y-1 items-center mb-2 w-full">
{#each ARG_TYPES as argType}
{@const isSelected = argType == property.selectedType}
<Button
size="sm"
variant="border"
color={isSelected ? 'blue' : 'dark'}
btnClasses={isSelected ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
on:click={() => {
property.selectedType = argType
property.format = undefined
property.contentEncoding = undefined
property.enum_ = undefined
property.pattern = undefined
}}
>
{argType}
</Button>
{/each}
<Button
size="sm"
variant="border"
color={isSelected ? 'blue' : 'dark'}
btnClasses={isSelected ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
color={!property.selectedType ? 'blue' : 'dark'}
btnClasses={!property.selectedType ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
on:click={() => {
property.selectedType = argType
property.format = undefined
property.contentEncoding = undefined
property.enum_ = undefined
property.pattern = undefined
property.selectedType = undefined
}}
>
{argType}
any
</Button>
{/each}
<Button
variant="border"
color={!property.selectedType ? 'blue' : 'dark'}
btnClasses={!property.selectedType ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
on:click={() => {
property.selectedType = undefined
}}
>
Any
</Button>
>
</div>
</div>
<Switch
label={'Required'}
textFormat={'text-md font-semibold text-gray-700'}
class="my-2"
class="my-2 !justify-start"
bind:checked={property.required}
/>
<ArgInput
@@ -168,27 +173,28 @@
pattern={property.pattern}
/>
{#if property.selectedType !== 'boolean'}
<h2 class="mb-2 mt-4">Advanced</h2>
{#if property.selectedType == 'string'}
<StringTypeNarrowing
bind:format={property.format}
bind:pattern={property.pattern}
bind:enum_={property.enum_}
bind:contentEncoding={property.contentEncoding}
/>
{:else if property.selectedType == 'array'}
<select bind:value={property.items}>
<option value={undefined}>No specific item type</option>
<option value={{ type: 'string' }}> Items are strings</option>
<option value={{ type: 'number' }}>Items are numbers</option>
</select>
{:else if property.selectedType == 'object'}
<h3 class="mb-2 font-bold mt-4">Resource type</h3>
<ObjectTypeNarrowing bind:format={property.format} />
{:else}
<p>No advanced configuration for this type</p>
{/if}
<div>
<div class="font-semibold text-gray-700 mb-1">Advanced</div>
{#if property.selectedType == 'string'}
<StringTypeNarrowing
bind:format={property.format}
bind:pattern={property.pattern}
bind:enum_={property.enum_}
bind:contentEncoding={property.contentEncoding}
/>
{:else if property.selectedType == 'array'}
<select bind:value={property.items}>
<option value={undefined}>No specific item type</option>
<option value={{ type: 'string' }}> Items are strings</option>
<option value={{ type: 'number' }}>Items are numbers</option>
</select>
{:else if property.selectedType == 'object'}
<h3 class="mb-2 font-bold mt-4">Resource type</h3>
<ObjectTypeNarrowing bind:format={property.format} />
{:else}
<p>No advanced configuration for this type</p>
{/if}
</div>
{/if}
</div>
</div>
@@ -150,6 +150,8 @@
{#if step === 1}
<CenteredPage>
<div class="space-y-6">
<h1 class="mb-4">New script</h1>
<h2 class="border-b pb-1 mt-4">General</h2>
<Path
bind:error={pathError}
bind:path={script.path}
@@ -165,7 +167,7 @@
<a href="https://docs.windmill.dev/docs/reference/namespaces">docs</a>
</div>
</Path>
<h3 class="text-gray-700 border-b">Language</h3>
<h2 class="border-b pb-1 mt-4">Language</h2>
<div class="max-w-md">
<RadioButton
label="Language"
@@ -178,7 +180,7 @@
bind:value={script.language}
/>
</div>
<h4 class="text-gray-700 border-b">
<h2 class="border-b pb-1 mt-4">
Script Kind <Tooltip
>In most cases, you will want the General Script. <br />
Trigger are meant to be used as the first module of flows to trigger them based on watching
@@ -188,7 +190,7 @@
Command scripts are used when the workspace is associated with a slack workspace to be triggered
on command.</Tooltip
>
</h4>
</h2>
{#if script.language == 'deno'}
<div class="max-w-lg">
@@ -226,11 +228,11 @@
{/if}
{#if script.language == 'deno' && script.kind == Script.kind.SCRIPT}
<h4 class="text-gray-700 border-b">
<h2 class="border-b pb-1 mt-4">
Script Template <Tooltip
>A template is a pre-filled script corresponding to a more specialized use-case</Tooltip
>
</h4>
</h2>
<div class="max-w-md">
<RadioButton
@@ -245,44 +247,6 @@
</div>
{/if}
<label class="block ">
<span class="text-gray-700">Summary <Required required={false} /></span>
<textarea
bind:value={script.summary}
class="
mt-1
block
w-full
rounded-md
border-gray-300
shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
"
placeholder="A very short summary of the script displayed when the script is listed"
rows="1"
/>
</label>
<label class="block" for="inp">
<span class="text-gray-700"
>Description<Required required={false} detail="accept markdown formatting" />
<textarea
id="inp"
bind:value={script.description}
class="
mt-1
block
w-full
rounded-md
border-gray-300
shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
"
placeholder="A description to help users understand what this script does and how to use it."
rows="3"
/>
</span>
</label>
<label class="block">
<span class="text-gray-700 mr-2"
>Save as workspace template <Tooltip
@@ -292,14 +256,39 @@
<input type="checkbox" bind:checked={script.is_template} />
</label>
<label class="block ">
<span class="text-gray-700 text-sm">Summary <Required required={false} /></span>
<textarea
bind:value={script.summary}
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300
focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
placeholder="A very short summary of the script displayed when the script is listed"
rows="1"
/>
</label>
<label class="block" for="inp">
<span class="text-gray-700 text-sm">
Description<Required required={false} detail="markdown" />
<textarea
id="inp"
bind:value={script.description}
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300
focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
placeholder="A description to help users understand what this script does and how to use it."
rows="3"
/>
</span>
</label>
<div>
<h3 class="text-gray-700 ">Description rendered</h3>
<div
class="prose mt-5 text-xs shadow-inner shadow-blue p-4 overflow-auto"
style="max-height: 200px;"
>
<SvelteMarkdown source={script.description ?? ''} />
</div>
<div class="font-bold pb-1 mt-4">Description preview</div>
{#if script.description}
<div class="prose max-h-48 mt-5 text-xs shadow-inner shadow-blue p-4 overflow-auto">
<SvelteMarkdown source={script.description} />
</div>
{:else}
<div class="text-sm text-gray-500"> Enter a description to see the preview </div>
{/if}
</div>
</div>
</CenteredPage>
@@ -15,9 +15,9 @@
import { HSplitPane, VSplitPane } from 'svelte-split-pane'
import { faGithub } from '@fortawesome/free-brands-svg-icons'
import EditorBar from './EditorBar.svelte'
import Button from './common/button/Button.svelte'
import TestJobLoader from './TestJobLoader.svelte'
import { onMount } from 'svelte'
import { Button, Kbd } from './common'
// Exported
export let schema: Schema = emptySchema()
@@ -100,7 +100,7 @@
<svelte:window on:keydown={onKeyDown} />
<div class="border-b shadow-sm p-1 pr-4">
<div class="border-b-2 shadow-sm p-1 pr-4">
<div class="flex justify-between">
<EditorBar {editor} {lang} {websocketAlive} />
@@ -159,12 +159,10 @@
<div class="h-full overflow-auto">
<div class="p-4">
<div class="break-all relative font-sans">
<div class="items-baseline text-xs text-gray-700 italic hidden md:block">
<p>
Move the focus outside of the text editor to recompute the input schema from
main signature or press Ctrl/Cmd+S
</p>
</div>
<p class="items-baseline break-normal text-sm text-gray-600 hidden md:block mb-3">
To recompute the input schema press <Kbd>Ctrl/Cmd</Kbd> + <Kbd>S</Kbd> or move the
focus outside of the text editor
</p>
<SchemaForm {schema} bind:args bind:isValid />
</div>
</div>
+21 -22
View File
@@ -21,37 +21,36 @@
</script>
<div class="w-full">
<PageHeader title="UI customisation" />
<h1 class="mb-4">UI customisation</h1>
<Tabs selected="ui">
<Tab value="ui">UI</Tab>
<Tab value="jsonschema">JSON Schema</Tab>
<svelte:fragment slot="content">
<TabContent value="ui">
<div class="grid grid-cols-3 gap-2">
<div>
<h2 class="mb-1">Summary</h2>
<div class="mb-2 md:mb-3 text-sm">
<textarea bind:value={summary} placeholder="Edit summary" />
<div class="prose text-sm">
{summary != '' ? summary : 'No summary'}
</div>
</div>
</div>
<div class="col-span-2">
<h2 class="mb-1">Description</h2>
<div class="mb-2 md:mb-6">
<div class="prose text-sm">
<textarea bind:value={description} placeholder="Edit description" />
<div class="prose text-sm">
<SvelteMarkdown
source={description && description != '' ? description : 'No description'}
/>
<h2 class="border-b pb-1 mt-6">Display</h2>
<div class="grid grid-cols-3 gap-2 mt-4">
<label>
<div class="text-gray-700 text-sm">Summary</div>
<textarea bind:value={summary} placeholder="Edit summary" class="text-sm" />
</label>
<label class="col-span-2">
<div class="text-gray-700 text-sm">Description</div>
<textarea bind:value={description} placeholder="Edit description" class="text-sm" />
<div class="mt-1 px-2">
{#if description}
<div
class="prose !max-w-full !max-h-48 text-xs shadow-inner shadow-blue overflow-auto mt-1"
>
<SvelteMarkdown source={description} />
</div>
</div>
{:else}
<div class="text-xs text-gray-500"> Enter a description to see the preview </div>
{/if}
</div>
</div>
</label>
</div>
<h2 class="border-b pb-1 mt-6">Arguments</h2>
<div class="bg-blue-100 border-l-4 border-blue-600 text-blue-700 p-4 m-4" role="alert">
<p class="font-bold">Synchronized with main signature</p>
<p>
+46 -42
View File
@@ -80,46 +80,50 @@
</script>
<Modal bind:this={modal}>
<div slot="title">Share {path}</div>
<div slot="title">
Share {path}
<span class="text-sm text-gray-500 ml-1">(group)</span>
</div>
<div slot="content">
<div class="flex flex-row pb-0 mb-5 justify-between">
<label class="block">
<span class="text-gray-700 text-sm"> Owner Kind </span>
<select
class="block mt-2 w-20"
bind:value={ownerKind}
on:change={() => {
if (ownerKind === 'group') {
owner = 'all'
} else {
owner = ''
}
}}
>
<option>user</option>
<option>group</option>
</select>
</label>
<label class="block grow mx-2" for="inp">
<span class="text-sm text-gray-700">Owner</span>
<div class="block mt-1">
<div class="static z-50">
<AutoComplete
items={ownerKind === 'user' ? usernames : groups}
bind:selectedItem={owner}
/>
<div class="flex flex-row flex-wrap pb-0 mb-5 justify-between">
<div class="flex gap-4 mr-2">
<label class="block">
<span class="text-gray-700 text-sm"> Owner Kind </span>
<select
class="block mt-1 w-20"
bind:value={ownerKind}
on:change={() => {
if (ownerKind === 'group') {
owner = 'all'
} else {
owner = ''
}
}}
>
<option>user</option>
<option>group</option>
</select>
</label>
<label class="block" for="inp">
<span class="text-sm text-gray-700">Owner</span>
<div class="block mt-1">
<div class="static z-50">
<AutoComplete
items={ownerKind === 'user' ? usernames : groups}
bind:selectedItem={owner}
/>
</div>
</div>
</div>
</label>
<label class="block">
<span class="text-sm text-gray-700">Editor</span>
<input class="block mt-4" type="checkbox" bind:checked={write} />
</label>
<Button size="sm" btnClasses="ml-2" on:click={() => addAcl(newOwner, write)}>
Add permission
</Button>
</label>
<label class="flex flex-col grow pb-3 justify-between">
<div class="text-sm text-gray-700">Editor</div>
<input class="block ml-1" type="checkbox" bind:checked={write} />
</label>
</div>
<div class="center-center">
<Button size="sm" on:click={() => addAcl(newOwner, write)}>Add permission</Button>
</div>
</div>
<TableCustom>
<tr slot="header-row">
@@ -132,11 +136,11 @@
<tr>
<td>{owner}</td>
<td>{write}</td>
<td
><Button variant="border" color="blue" size="sm" on:click={() => deleteAcl(owner)}
>Delete</Button
></td
>
<td>
<Button variant="border" color="red" size="sm" on:click={() => deleteAcl(owner)}>
Delete
</Button>
</td>
</tr>
{/each}
</tbody>
@@ -115,59 +115,63 @@
</div>
<div slot="content">
<div class="text-gray-700 text-2xs mb-6">
Variables have a globally unique name represented by their path. When passed to scripts, <pre
class="inline text-red-700 bg-gray-50 rounded round-sm">/</pre
>
are converted to
<pre class="inline text-red-700 bg-gray-50 rounded round-sm">_</pre>
</div>
<div class="flex flex-col gap-2 ">
<div class="flex flex-col gap-6">
<div>
<div class="text-gray-700 mb-0 pb-0">path</div>
<Path
bind:error={pathError}
bind:path
{initialPath}
namePlaceholder="my_variable"
kind="variable"
/>
<div>
<div class="mb-1 font-semibold text-gray-700">General</div>
<Path
bind:error={pathError}
bind:path
{initialPath}
namePlaceholder="my_variable"
kind="variable"
/>
</div>
<div class="mt-4">
<label class="block">
<span class="text-sm mr-1">Secret</span>
<input type="checkbox" bind:checked={variable.is_secret} />
</label>
{#if variable.is_secret}
<div
class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-1 mt-1 text-sm"
role="alert"
>
If the variable is a secret, you will not be able to read the value of it from the
variable editor UI but only within scripts.
<Tooltip>
Within scripts, every read of the value create the audit log:
'variables.decrypt_secret'
</Tooltip>
</div>
{/if}
</div>
</div>
<label class="block pb-6">
<input type="checkbox" bind:checked={variable.is_secret} />
<span class="ml-2">Secret</span>
{#if variable.is_secret}
<div
class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-1 mt-1 text-sm"
role="alert"
>
When a secret, you will not be able to read this variable value from the variable editor
UI but only within scripts. <Tooltip>
Within scripts, every read of the value create the audit log:
'variables.decrypt_secret'</Tooltip
>
</div>
{/if}
</label>
{#if variable.is_secret}
<div class="font-semibold text-gray-700 col-span-10 }">
<div class="mb-1 col-span-10">
<Password
bind:password={variable.value}
placeholder={'******** (only fill to update value)'}
label={`Secret value (${variable.value.length}/3000 characters)`}
label={`<span class="font-semibold text-gray-700">Secret value</span>
<span class="text-sm text-gray-500">(${variable.value.length}/3000 characters)</span>`}
/>
</div>
{:else}
<div>
<span>Variable value ({variable.value.length}/3000 characters)</span>
<div class="mb-1">
<span class="font-semibold text-gray-700">Variable value</span>
<span class="text-sm text-gray-500">({variable.value.length}/3000 characters)</span>
</div>
<AutosizedTextarea bind:value={variable.value} minRows={5} />
</div>
{/if}
<div class="flex flex-col w-full">
<span class="text-gray-700">Description<Required required={false} /></span>
<div>
<div class="mb-1 font-semibold text-gray-700">
Description
<Required required={false} />
</div>
<AutosizedTextarea bind:value={variable.description} placeholder={''} minRows={3} />
</div>
</div>
@@ -51,11 +51,10 @@
variant === 'border' ? 'border' : '',
ButtonType.FontSizeClasses[size],
ButtonType.SpacingClasses[spacingSize],
'focus:ring-4 font-medium',
'focus:ring-4 font-semibold',
'rounded-md',
'flex justify-center items-center text-center whitespace-nowrap',
btnClasses,
disabled ? 'pointer-events-none cursor-default filter grayscale' : ''
btnClasses
),
disabled,
href,
@@ -1,5 +1,5 @@
<kbd
class="mx-1 px-2 py-1.5 text-xs font-semibold text-gray-800
class="mx-1 px-2 py-1 text-xs font-mono font-semibold text-gray-800
bg-gray-100 border border-gray-200 rounded-lg
dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500 {$$props.class}"
>
@@ -17,9 +17,7 @@
}
</script>
<div
class="flex items-center justify-between py-2 px-4 border-b bg-gray-50 shadow-sm space-x-2 h-12 flex-nowrap"
>
<div class="flex items-center justify-between py-2 px-4 border-b space-x-2 h-12 flex-nowrap">
{#if flowModule}
<span class="text-sm w-full">
<div class="flex items-center space-x-2">
@@ -143,7 +143,7 @@
/>
{:else}
{#if flowModule.value.type === 'rawscript'}
<div class="flex-shrink-0 border-b p-1">
<div class="flex-shrink-0 border-b-2 shadow-sm p-1 mb-1">
<EditorBar
{editor}
lang={flowModule.value['language'] ?? 'deno'}
@@ -35,7 +35,7 @@
</Path>
<label class="block my-4">
<span class="text-gray-700">Summary <Required required={false} /></span>
<span class="text-gray-700 text-sm">Summary <Required required={false} /></span>
<textarea
bind:value={$flowStore.summary}
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
@@ -46,20 +46,14 @@
</label>
<label class="block my-4" for="inp">
<span class="text-gray-700"
>Description<Required required={false} detail="accept markdown formatting" />
<span class="text-gray-700 text-sm">
Description
<Required required={false} detail="markdown" />
<textarea
id="inp"
bind:value={$flowStore.description}
class="
mt-1
block
w-full
rounded-md
border-gray-300
shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300
focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
placeholder="A description to help users understand what this script does and how to use it."
rows="3"
/>
@@ -67,13 +61,14 @@
</label>
<div>
<h3 class="text-gray-700 ">Description rendered</h3>
<div
class="prose mt-5 text-xs shadow-inner shadow-blue p-4 overflow-auto"
style="max-height: 200px;"
>
<SvelteMarkdown source={$flowStore.description ?? ''} />
</div>
<div class="font-bold pb-1 mt-4">Description preview</div>
{#if $flowStore.description}
<div class="prose max-h-48 mt-5 text-xs shadow-inner shadow-blue p-4 overflow-auto">
<SvelteMarkdown source={$flowStore.description} />
</div>
{:else}
<div class="text-sm text-gray-500"> Enter a description to see the preview </div>
{/if}
</div>
</TabContent>
<TabContent value="schedule" class="p-4">
@@ -29,7 +29,8 @@
<Menu placement="bottom-end">
<button
slot="trigger"
class="flex items-center text-sm border focus:outline-none focus:ring-4 font-medium rounded-md text-gray-800 bg-white hover:bg-gray-100 focus:ring-gray-300 px-4 py-2"
class="flex items-center text-sm border focus:outline-none focus:ring-4 rounded-md
text-gray-800 bg-white hover:bg-gray-100 focus:ring-gray-300 px-4 py-2"
>
Import/Export
</button>
@@ -82,7 +82,7 @@
{/if}
</div>
<h3 class="break-all py-2">
<div class="break-all py-2">
{#if job.script_path}
<a class="pr-3" href="/run/{job.id}">{job.script_path} </a>
{:else if 'job_kind' in job && job.job_kind == 'preview'}
@@ -104,7 +104,7 @@
><a href="/run/{job.parent_job}">step of flow</a></span
>
{/if}
</h3>
</div>
{/if}
</div>
<div>
@@ -7,7 +7,7 @@
<div class="p-4 border border-gray-300 rounded-md bg-gray-50 ">
<div class="flex items-center">
<h3 class="inline-flex items-center text-lg font-medium text-gray-700 ">
<h3 class="inline-flex items-center font-semibold">
<HatIcon class="w-6 h-6 mr-2" />
Getting started
</h3>
@@ -7,7 +7,7 @@
<div class="p-4 border border-gray-300 rounded-md bg-gray-50 dark:border-gray-600 dark:bg-gray-700">
<div class="flex items-center">
<h3 class="inline-flex items-center text-lg font-medium text-gray-700 ">
<h3 class="inline-flex items-center font-semibold">
<HatIcon class="w-6 h-6 mr-2" />
Getting started
</h3>
@@ -7,7 +7,7 @@
<div class="p-4 border border-gray-300 rounded-md bg-gray-50 ">
<div class="flex items-center">
<h3 class="inline-flex items-center text-lg font-medium text-gray-700 ">
<h3 class="inline-flex items-center font-semibold">
<HatIcon class="w-6 h-6 mr-2" />
Getting started
</h3>
@@ -47,7 +47,10 @@
<ul class="w-full">
{#each keys as key, index}
<li class="pt-1">
<button on:click={() => selectProp(key)} class="key rounded px-1 hover:bg-blue-100">
<button
on:click={() => selectProp(key)}
class="key font-normal rounded px-1 hover:bg-blue-100"
>
{!isArray ? key : index}:
</button>
@@ -28,20 +28,22 @@
</button>
<div class="divide-y divide-gray-100" role="none">
{#each $usersWorkspaceStore?.workspaces ?? [] as workspace}
<button
on:click={() => {
workspaceStore.set(workspace.id)
close()
}}
class="block px-4 py-2 text-xs text-gray-500 "
role="menuitem"
tabindex="-1"
>
<span class="text-gray-300 font-mono pr-1 text-xs">{workspace.id}</span>
{workspace.name}
</button>
{/each}
<table class="w-full">
{#each $usersWorkspaceStore?.workspaces ?? [] as workspace}
<tr
class="text-xs cursor-pointer"
on:click={() => {
workspaceStore.set(workspace.id)
close()
}}
>
<td class="text-gray-300 font-mono pl-4 pr-1 py-2 text-xs whitespace-nowrap"
>{workspace.id}</td
>
<td class="text-gray-500 pr-4 py-2 w-full">{workspace.name}</td>
</tr>
{/each}
</table>
<div class="py-1" role="none">
<a
href="/user/create_workspace"
+13 -12
View File
@@ -179,23 +179,24 @@
{#each tab == 'all' ? ['personal', 'groups', 'shared', 'hub'] : [tab] as sectionTab}
<div class="shadow p-4 my-2">
{#if sectionTab == 'personal'}
<h2 class="mb-2">
Personal <span class="text-sm"
>({`u/${$userStore?.username}`}) <Tooltip>
<h2>
<span class="text-lg xl:text-xl">Personal</span>
<span class="text-sm">
({`u/${$userStore?.username}`}) <Tooltip>
All flows owned by you (and visible only to you if you do not explicitly share them)
</Tooltip></span
>
</h2>
{:else if sectionTab == 'groups'}
<h2 class="">
<h2 class="text-lg xl:text-xl">
Groups <Tooltip>All flows being owned by groups that you are member of</Tooltip>
</h2>
{:else if sectionTab == 'shared'}
<h2 class="">
<h2 class="text-lg xl:text-xl">
Shared <Tooltip>All flows visible to you because they have been shared to you</Tooltip>
</h2>
{:else if sectionTab == 'hub'}
<h2 class="">
<h2 class="text-lg xl:text-xl">
Approved flows from the WindmillHub <Tooltip>
All approved Flow from the <a href="https://hub.windmill.dev">WindmillHub</a>.
Approved flows have been potentially contributed by the community but reviewed and
@@ -203,7 +204,7 @@
</Tooltip>
</h2>
<input placeholder="Search hub flows" bind:value={hubFilter} class="search-bar mt-2" />
<div class="relative">
<div class="relative mt-2">
{#if loading.hub}
<Skeleton
loading={loading.hub}
@@ -249,7 +250,7 @@
{/if}
{#each groupedFlows.filter((x) => tabFromPath(x[0]) == sectionTab) as [section, flows]}
{#if sectionTab != 'personal'}
<h3 class="mt-2 mb-2">
<div class="font-bold text-gray-700 mt-2 mb-2">
{section}
{#if section == 'g/all'}
<Tooltip
@@ -258,18 +259,18 @@
are private user namespaces.</Tooltip
>
{/if}
</h3>
</div>
{/if}
{#if loading.general}
<div class="grid gap-4 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-3">
<div class="grid gap-4 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-3 mt-2">
{#each new Array(3) as _}
<Skeleton layout={[[7.6]]} />
{/each}
</div>
{:else if flows.length == 0 && sectionTab == 'personal'}
<p class="text-xs text-gray-600 italic">No flows yet</p>
<p class="text-xs text-gray-600 italic mt-2">No flows yet</p>
{:else}
<div class="grid md:grid-cols-2 gap-4 sm:grid-cols-1 xl:grid-cols-3">
<div class="grid md:grid-cols-2 gap-4 sm:grid-cols-1 xl:grid-cols-3 mt-2">
{#each flows as { summary, path, extra_perms, canWrite }}
<div
class="flex flex-col justify-between max-w-lg overflow-visible
+9 -25
View File
@@ -55,15 +55,11 @@
</script>
<CenteredPage>
<h1 class="text-xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl">Home</h1>
<h1 class="flex items-center min-h-[48px] font-black my-4">Home</h1>
<div class="space-y-12">
<div>
<h2
class="mb-4 text-lg font-extrabold tracking-tight leading-none text-gray-900 md:text-2xl border-b py-2"
>
<span class="text-transparent bg-clip-text bg-gradient-to-r to-blue-500 from-blue-600">
Scripts
</span>
<h2 class="border-b mb-4 py-2">
<span class="text-blue-gradient">Scripts</span>
</h2>
<ScriptGettingStarted />
@@ -95,12 +91,8 @@
</div>
</div>
<div>
<h2
class="mb-4 text-lg font-extrabold tracking-tight leading-none text-gray-900 md:text-2xl border-b py-2 "
>
<span class="text-transparent bg-clip-text bg-gradient-to-r to-blue-500 from-blue-600">
Flows
</span>
<h2 class="border-b mb-4 py-2">
<span class="text-blue-gradient">Flows</span>
</h2>
<FlowGettingStarted />
<div class="mt-6 mb-2 text-md font-bold text-gray-900 ">Latest flows:</div>
@@ -132,12 +124,8 @@
</div>
</div>
<div>
<h2
class="mb-4 text-lg font-extrabold tracking-tight leading-none text-gray-900 md:text-2xl border-b py-2 "
>
<span class="text-transparent bg-clip-text bg-gradient-to-r to-blue-500 from-blue-600">
Resources
</span>
<h2 class="border-b mb-4 py-2">
<span class="text-blue-gradient">Resources</span>
</h2>
{#if resources.length === 0}
@@ -145,12 +133,8 @@
{/if}
</div>
<div>
<h2
class="mb-4 text-lg font-extrabold tracking-tight leading-none text-gray-900 md:text-2xl border-b py-2 "
>
<span class="text-transparent bg-clip-text bg-gradient-to-r to-blue-500 from-blue-600">
Runs
</span>
<h2 class="border-b mb-4 py-2">
<span class="text-blue-gradient">Runs</span>
</h2>
<div class="grid grid-cols-1 gap-4 my-4">
+20 -16
View File
@@ -334,24 +334,28 @@
<div slot="title">{resourceViewerTitle}</div>
<div slot="content">
{#if typeModalMode === 'create'}
<label for="inp"
><h3 class="font-semibold text-gray-700">Name<Required required={true} /></h3>
<div>
<span
class="border border-gray-700 rounded p-1 -mr-4 text-sm bg-gray-200 inline-block w-8"
>c_</span
>
<div class="inline-block">
<input id="inp" type="text" bind:value={newResourceTypeName} />
<div class="flex flex-col gap-6">
<label for="inp">
<div class="mb-1 font-semibold text-gray-700">Name<Required required={true} /></div>
<div>
<span
class="border border-gray-700 rounded p-1 -mr-4 text-sm bg-gray-200 inline-block w-8"
>c_</span
>
<div class="inline-block">
<input id="inp" type="text" bind:value={newResourceTypeName} />
</div>
</div>
</label>
<label>
<div class="mb-1 font-semibold text-gray-700">Description</div>
<input type="text" bind:value={newResourceTypeDescription} /></label
>
<div>
<div class="mb-1 font-semibold text-gray-700">Schema</div>
<SchemaEditor bind:schema={newResourceTypeSchema} />
</div>
</label>
<label
><h3 class="mt-4 font-semibold text-gray-700">Description</h3>
<input type="text" bind:value={newResourceTypeDescription} /></label
>
<h3 class="mt-4 mb-2 font-semibold text-gray-700">Schema</h3>
<SchemaEditor bind:schema={newResourceTypeSchema} />
</div>
{:else if typeModalMode === 'view'}
<Highlight language={json} code={JSON.stringify(resourceViewerSchema, null, 4)} />
{:else if typeModalMode === 'view-type'}
+14 -11
View File
@@ -20,6 +20,7 @@
import Tab from '$lib/components/common/tabs/Tab.svelte'
import JobDetail from '$lib/components/jobs/JobDetail.svelte'
import { Skeleton } from '$lib/components/common'
import Tooltip from '../../lib/components/Tooltip.svelte'
let jobs: Job[] | undefined
let error: Error | undefined
@@ -145,17 +146,19 @@
</script>
<CenteredPage>
<PageHeader
title="Runs {path ? `of ${path}` : ''}"
tooltip="Below is NOT all the runs that have ever been run. You only see the runs whose execution has
been permissioned with privilege that you are allowed to see. In most cases, it will only be
your personal runs, scheduled runs of groups that you are member of, and runs that are
permissioned at the group level of a group you are a member of. Hence, you can safely run
script with sensitive logs knowing that only the users with the relevant permissions would see
it. The permission of a run constraint the ephemeral bearer token that is passed to at
execution time of that run. This is why runs with less permissions are less sensitive because
the bearer token they use has less privilege."
/>
<div class="flex items-center min-h-[48px] my-4">
<h1 class="mr-1">Runs {path ? `of ${path}` : ''}</h1>
<Tooltip>
Below is NOT all the runs that have ever been run. You only see the runs whose execution has
been permissioned with privilege that you are allowed to see. In most cases, it will only be
your personal runs, scheduled runs of groups that you are member of, and runs that are
permissioned at the group level of a group you are a member of. Hence, you can safely run
script with sensitive logs knowing that only the users with the relevant permissions would see
it. The permission of a run constraint the ephemeral bearer token that is passed to at
execution time of that run. This is why runs with less permissions are less sensitive because
the bearer token they use has less privilege.
</Tooltip>
</div>
<div class="max-w-7x">
<div class="flex flex-row space-x-4">
+8 -7
View File
@@ -111,11 +111,11 @@
</script>
<CenteredPage>
<PageHeader title={edit ? 'Edit schedule ' + path : 'New schedule'} />
<h1 class="mb-4">{edit ? 'Edit schedule ' + path : 'New schedule'}</h1>
<div>
{#if !edit}
<h2>Save schedule as</h2>
<h2 class="border-b pb-1 mt-8 mb-2">Save schedule as</h2>
<Path
bind:error={pathError}
@@ -134,7 +134,7 @@
>
{/if}
<h2 class="my-2 md:mt-6">Script</h2>
<h2 class="border-b pb-1 mt-8 mb-2">Script</h2>
<p class="text-xs mb-1 text-gray-600">
Pick a script or flow to be triggered by the schedule<Required required={true} />
</p>
@@ -144,8 +144,8 @@
bind:itemKind
bind:scriptPath={script_path}
/>
<div class="max-w-5xl {edit ? '' : 'mt-2 md:mt-6'}">
<h2>Arguments</h2>
<div class={edit ? '' : 'mt-2 md:mt-6'}>
<h2 class="border-b pb-1 mt-8 mb-2">Arguments</h2>
{#if runnable}
{#if runnable?.schema && runnable.schema.properties && Object.keys(runnable.schema.properties).length > 0}
<SchemaForm schema={runnable.schema} bind:isValid bind:args />
@@ -160,8 +160,9 @@
</div>
{/if}
</div>
<h2 class="mt-2 md:mt-6">
Schedule<Tooltip>Schedules use CRON syntax. Seconds are mandatory.</Tooltip>
<h2 class="border-b pb-1 mt-8 mb-2">
<span class="mr-1">Schedule</span>
<Tooltip>Schedules use CRON syntax. Seconds are mandatory.</Tooltip>
</h2>
<CronInput bind:schedule bind:validCRON />
<div class="flex flex-row-reverse mt-2 ">
+1 -1
View File
@@ -63,7 +63,7 @@
</script>
<CenteredPage>
<PageHeader title="Schedule">
<PageHeader title="Schedules">
<Button size="sm" startIcon={{ icon: faPlus }} href="/schedule/add">New&nbsp;schedule</Button>
</PageHeader>
<div class="relative mt-10">
+15 -14
View File
@@ -187,32 +187,33 @@
{#each tab == 'all' ? ['personal', 'groups', 'shared', 'examples', 'hub'] : [tab] as sectionTab}
<div class="shadow p-4 my-2">
{#if sectionTab == 'personal'}
<h2 class="mb-2">
Personal <span class="text-sm"
>({`u/${$userStore?.username}`}) <Tooltip>
<h2>
<span class="text-lg xl:text-xl">Personal</span>
<span class="text-sm">
({`u/${$userStore?.username}`}) <Tooltip>
All scripts owned by you (and visible only to you if you do not explicitly share
them)
</Tooltip></span
>
</Tooltip>
</span>
</h2>
{:else if sectionTab == 'groups'}
<h2 class="">
<h2 class="text-lg xl:text-xl">
Groups <Tooltip>All scripts being owned by groups that you are member of</Tooltip>
</h2>
{:else if sectionTab == 'shared'}
<h2 class="">
<h2 class="text-lg xl:text-xl">
Shared <Tooltip>All scripts visible to you because they have been shared to you</Tooltip
>
</h2>
{:else if sectionTab == 'examples'}
<h2 class="">
<h2 class="text-lg xl:text-xl">
Public <Tooltip>
Template and examples shared across all workspaces of this instance. They are managed
from a special workspace called 'starter' that only superadmin can change.
</Tooltip>
</h2>
{:else if sectionTab == 'hub'}
<h2 class="">
<h2 class="text-lg xl:text-xl">
Approved scripts from the WindmillHub <Tooltip>
All approved Deno scripts from the <a href="https://hub.windmill.dev">WindmillHub</a>.
Approved scripts have been reviewed by the Windmill team and are safe to use in
@@ -266,7 +267,7 @@
{/if}
{#each sectionTab == 'examples' ? communityScripts : groupedScripts.filter((x) => tabFromPath(x[0]) == sectionTab) as [section, scripts]}
{#if sectionTab != 'personal' && sectionTab != 'examples'}
<h3 class="mt-2 mb-2">
<div class="font-bold text-gray-700 mt-2 mb-2">
{section}
{#if section == 'g/all'}
<Tooltip
@@ -275,18 +276,18 @@
are private user namespaces.</Tooltip
>
{/if}
</h3>
</div>
{/if}
{#if loading}
<div class="grid gap-4 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-3">
<div class="grid gap-4 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-3 mt-2">
{#each new Array(3) as _}
<Skeleton layout={[[8.5]]} />
{/each}
</div>
{:else if scripts.length == 0 && sectionTab == 'personal'}
<p class="text-xs text-gray-600 italic">No scripts yet</p>
<p class="text-xs text-gray-600 italic mt-2">No scripts yet</p>
{:else}
<div class="grid md:grid-cols-2 gap-4 sm:grid-cols-1 xl:grid-cols-3">
<div class="grid md:grid-cols-2 gap-4 sm:grid-cols-1 xl:grid-cols-3 mt-2">
{#each scripts as { summary, path, hash, language, extra_perms, canWrite, lock_error_logs, kind }}
<div
class="flex flex-col justify-between gap-2 max-w-lg overflow-visible shadow-sm shadow-blue-100
@@ -391,8 +391,8 @@
{/if}
</div>
</div>
<div>
<h3 class="text-lg mb-1 font-bold text-gray-600">
<div class="pt-4">
<h2 class="text-lg mb-1 font-bold text-gray-600">
Arguments JSON schema
<Tooltip>
The jsonschema defines the constraints that the payload must respect to be compatible with
@@ -400,21 +400,21 @@
script jsonschema. See
<a href="https://json-schema.org/"> jsonschema documentation </a>
</Tooltip>
</h3>
</h2>
<Skeleton {loading} layout={[[15]]} />
{#if script}
<SchemaViewer schema={script.schema} />
{/if}
</div>
<div>
<h3 class="text-lg mb-1 font-bold text-gray-600">Code</h3>
<h2 class="text-lg mb-1 font-bold text-gray-600 border-b">Code</h2>
<Skeleton {loading} layout={[[20]]} />
{#if script}
<HighlightCode language={script.language} code={script.content} />
{/if}
</div>
<div>
<h3 class="text-lg mb-1 font-bold text-gray-600">Dependencies lock file</h3>
<h2 class="text-lg mb-1 font-bold text-gray-600 border-b">Dependencies lock file</h2>
<Skeleton {loading} layout={[[5]]} />
{#if script}
{#if script?.lock}
@@ -94,6 +94,7 @@
border-gray-300
shadow-sm
text-sm
font-normal
mt-1
hover:ring-1
hover:ring-indigo-300
@@ -123,7 +124,6 @@
{#each invites as invite}
<div
class="
block
w-full
mx-auto
py-1
@@ -151,7 +151,7 @@
? `&rd=${encodeURIComponent(rd)}`
: ''}"
>
accept
Accept
</a>
<button
@@ -164,7 +164,7 @@
loadInvites()
}}
>
decline
Decline
</button>
</span>
</div>
@@ -186,7 +186,7 @@
logout()
}}
>
logout
Logout
</Button>
</div>
</CenteredModal>
+2 -2
View File
@@ -54,10 +54,10 @@
<p>No workers seems to be available</p>
{/if}
{#each groupedWorkers as [section, workers]}
<h2 class="my-5">
<div class="mt-6">
Instance: {section} | IP:
<Badge large color="gray">{workers[0].ip}</Badge>
</h2>
</div>
<TableCustom>
<tr slot="header-row">
Binary file not shown.
+100 -16
View File
@@ -7,7 +7,7 @@ const config = {
"./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}",
],
safelist: [
'inline-highlight'
'hljs'
],
theme: {
colors: {
@@ -90,6 +90,11 @@ const config = {
900: '#312e81',
}
},
fontFamily: {
// add double quotes if there is space in font name
main: ['Inter', 'sans-serif'],
mono: ['ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', '"Liberation Mono"', '"Courier New"', 'monospace']
},
extend: {
maxHeight: {
'1/2': '50vh',
@@ -115,7 +120,12 @@ const config = {
},
fontSize: {
'2xs': '0.7rem'
}
},
screens: {
'fhd': '1900px',
'qhd': '2500px',
'4k': '3800px',
},
}
},
@@ -126,15 +136,83 @@ const config = {
plugin(({ addBase, addComponents, addUtilities, theme }) => {
addBase({
'html': {
overflowY: 'auto'
overflowY: 'auto',
fontFamily: theme('fontFamily.main'),
fontSize: theme('fontSize.base'),
fontWeight: theme('fontWeight.normal'),
color: theme('colors.gray.900'),
[`@media (min-width: ${theme('screens.qhd')})`]: {
fontSize: theme('fontSize.lg'),
},
},
'h1': {
fontSize: theme('fontSize.2xl'),
color: theme('colors.gray.700')
fontSize: '24px',
fontWeight: theme('fontWeight.extrabold'),
lineHeight: '1.05',
color: theme('colors.gray.800'),
[`@media (min-width: ${theme('screens.lg')})`]: {
fontSize: '26px',
},
[`@media (min-width: ${theme('screens.fhd')})`]: {
fontSize: '29px',
},
[`@media (min-width: ${theme('screens.qhd')})`]: {
fontSize: '34px',
},
},
'h1': {
fontSize: theme('fontSize.xl'),
color: theme('colors.blue.500')
'h2': {
fontSize: '20px',
fontWeight: theme('fontWeight.extrabold'),
lineHeight: '1.1',
color: theme('colors.gray.700'),
[`@media (min-width: ${theme('screens.fhd')})`]: {
fontSize: '22px',
},
[`@media (min-width: ${theme('screens.qhd')})`]: {
fontSize: '25px',
},
},
'h3': {
fontSize: '18px',
fontWeight: theme('fontWeight.bold'),
lineHeight: '1.2',
color: theme('colors.gray.600'),
[`@media (min-width: ${theme('screens.fhd')})`]: {
fontSize: '20px',
},
[`@media (min-width: ${theme('screens.qhd')})`]: {
fontSize: '22px',
},
},
'h4': {
fontSize: '18px',
fontWeight: theme('fontWeight.semibold'),
lineHeight: '1.3',
color: theme('colors.gray.600'),
[`@media (min-width: ${theme('screens.qhd')})`]: {
fontSize: '20px',
},
},
'h5': {
fontSize: '16px',
fontWeight: theme('fontWeight.semibold'),
lineHeight: '1.5',
color: theme('colors.gray.600'),
[`@media (min-width: ${theme('screens.qhd')})`]: {
fontSize: '18px',
},
},
'h6': {
fontSize: '16px',
fontWeight: theme('fontWeight.medium'),
lineHeight: '1.5',
color: theme('colors.gray.600'),
[`@media (min-width: ${theme('screens.qhd')})`]: {
fontSize: '18px',
},
},
'button': {
fontWeight: theme('fontWeight.semibold'),
},
'a': {
color: theme('colors.blue.500')
@@ -161,7 +239,14 @@ const config = {
pointerEvents: 'none',
cursor: 'default',
filter: 'grayscale(1)'
}
},
'pre code.hljs': {
padding: '0px !important',
fontFamily: theme('fontFamily.mono'),
fontSize: theme('fontSize.sm') + ' !important',
lineHeight: theme('lineHeight.4') + ' !important',
whiteSpace: 'pre-wrap'
},
});
addComponents({
'#table-custom': {
@@ -199,17 +284,16 @@ const config = {
boxShadow: theme('boxShadow.sm'),
padding: theme('spacing.4')
},
'pre code.hljs': {
padding: '0px',
fontSize: theme('fontSize.xs'),
lineHeight: theme('lineHeight.4'),
whiteSpace: 'pre-wrap'
},
'.animate-skeleton': {
animation: theme('animation.pulse'),
backgroundColor: theme('colors.blue.200'),
borderRadius: theme('borderRadius.DEFAULT'),
}
},
'.text-blue-gradient': {
color: 'transparent',
backgroundClip: 'text',
backgroundImage: `linear-gradient(to right, ${theme('colors.blue.600')}, ${theme('colors.blue.500')})`
}
});
addUtilities({
'.separator': {