mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
feat: favorite datatable and ducklake tables + asset page nits (#7795)
* update cf worker hostname
* set remote_url cookie from param
* ephemeral backends v1
* nit
* Run queue server
* ntis
* timeout
* better db process management
* commit hash and worktree
* nit use map
* nit
* err handling
* Revert "err handling"
This reverts commit 19de00c0c0.
* nits
* auto cleanup
* Ephemeral backend command action
* remove checkout
* checkout ee repo
* nits
* process.env.GIT_EE_DEPLOY_KEY_FILE
* resumeURLs logic
* nit
* use windmill flow for ephemeral backend action
* fixes
* new token
* worktree pools
* Delete GH secret on cleanup
* linux deploy
* nit
* nit
* unhandled promises
* nit
* fix docker bridge IP on linux
* pass cf_frontend_url to wmill flow
* git fetch
* release worktree when binary started
* send error
* logger
* logging
* logging 2
* delete log files periodically
* redirect to raw app with logs
* CORS
* MANAGER_AUTH_TOKEN
* Check organization membership
* nit
* bwrap
* nit
* return timeoutAt in resumeUrl
* nit
* Change password
* nit remove https
* Settings icon instead of plain text
* doc link
* favorite icon
* Better favorites state logic
* Asset favorites
* nit fixes and better label display
* global db manager drawer
* Favorites menu open db manager
* favorites in assets page + backend refactor
* Delete favorite dropdown
* parseFavoriteAsset
* git fail
* nit
* git fail
* nit
* nits
* git fail
* Table2 instead of pyramid
This commit is contained in:
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,10 @@
|
||||
-- Add up migration script here
|
||||
|
||||
DO
|
||||
$do$
|
||||
BEGIN
|
||||
ALTER TYPE FAVORITE_KIND ADD VALUE 'asset';
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE NOTICE 'Couldn''t create FAVORITE_KIND::asset: %', SQLERRM;
|
||||
END
|
||||
$do$;
|
||||
@@ -15018,7 +15018,7 @@ paths:
|
||||
type: string
|
||||
favorite_kind:
|
||||
type: string
|
||||
enum: [flow, app, script, raw_app]
|
||||
enum: [flow, app, script, raw_app, asset]
|
||||
responses:
|
||||
"200":
|
||||
description: star item
|
||||
@@ -15041,7 +15041,7 @@ paths:
|
||||
type: string
|
||||
favorite_kind:
|
||||
type: string
|
||||
enum: [flow, app, script, raw_app]
|
||||
enum: [flow, app, script, raw_app, asset]
|
||||
responses:
|
||||
"200":
|
||||
description: unstar item
|
||||
@@ -16671,6 +16671,30 @@ paths:
|
||||
access_type:
|
||||
$ref: "#/components/schemas/AssetUsageAccessType"
|
||||
|
||||
/w/{workspace}/assets/list_favorites:
|
||||
get:
|
||||
summary: List all favorite assets for the authenticated user
|
||||
operationId: listFavoriteAssets
|
||||
tags:
|
||||
- asset
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
responses:
|
||||
"200":
|
||||
description: list of favorite assets
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [path]
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
description: The asset path
|
||||
|
||||
|
||||
/mcp/w/{workspace}/list_tools:
|
||||
get:
|
||||
summary: list available MCP tools
|
||||
|
||||
@@ -18,6 +18,7 @@ pub fn workspaced_service() -> Router {
|
||||
Router::new()
|
||||
.route("/list", get(list_assets))
|
||||
.route("/list_by_usages", post(list_assets_by_usages))
|
||||
.route("/list_favorites", get(list_favorites))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -286,3 +287,29 @@ async fn list_assets_by_usages(
|
||||
}
|
||||
Ok(Json(assets_vec))
|
||||
}
|
||||
|
||||
async fn list_favorites(
|
||||
authed: ApiAuthed,
|
||||
Path(w_id): Path<String>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
) -> JsonResult<Vec<Value>> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let favorites = sqlx::query_scalar!(
|
||||
r#"SELECT
|
||||
jsonb_strip_nulls(jsonb_build_object(
|
||||
'path', favorite.path
|
||||
)) as "favorite_asset!: _"
|
||||
FROM favorite
|
||||
WHERE favorite.workspace_id = $1
|
||||
AND favorite.usr = $2
|
||||
AND favorite_kind = 'asset'
|
||||
"#,
|
||||
&w_id,
|
||||
&authed.username
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
|
||||
Ok(Json(favorites))
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ pub enum FavoriteKind {
|
||||
App,
|
||||
#[allow(non_camel_case_types)]
|
||||
Raw_App,
|
||||
Asset,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct Favorite {
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
} else if (kind === 'folder') {
|
||||
const folder = await FolderService.getFolder({ workspace, name: path.slice(2) })
|
||||
return folder.summary
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch summary for ${kind}:${path}`, error)
|
||||
@@ -122,7 +121,9 @@
|
||||
|
||||
async function fetchSummaries(diffs: WorkspaceItemDiff[]) {
|
||||
// Only fetch summaries for scripts, flows, and apps
|
||||
const itemsToFetch = diffs.filter((diff) => ['script', 'flow', 'app', 'folder'].includes(diff.kind))
|
||||
const itemsToFetch = diffs.filter((diff) =>
|
||||
['script', 'flow', 'app', 'folder'].includes(diff.kind)
|
||||
)
|
||||
|
||||
for (const diff of itemsToFetch) {
|
||||
const key = getItemKey(diff)
|
||||
@@ -915,7 +916,6 @@
|
||||
kind={diff.kind}
|
||||
canFavorite={false}
|
||||
workspaceId=""
|
||||
starred={false}
|
||||
>
|
||||
{#snippet customSummary()}
|
||||
{#if oldSummary != newSummary && isSelectable && existsInBothWorkspaces}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
import { resource } from 'runed'
|
||||
import { capitalize, onlyAlphaNumAndUnderscore, pluralize } from '$lib/utils'
|
||||
import type { DbFeatures } from './apps/components/display/dbtable/dbFeatures'
|
||||
import Star from './Star.svelte'
|
||||
import type { Asset } from '$lib/gen'
|
||||
|
||||
/** Represents a selected table with its schema */
|
||||
export interface SelectedTable {
|
||||
@@ -50,6 +52,7 @@
|
||||
/** Tables that are already added and should show as disabled */
|
||||
disabledTables?: SelectedTable[]
|
||||
features?: DbFeatures
|
||||
asset?: Asset
|
||||
}
|
||||
let {
|
||||
dbType,
|
||||
@@ -67,7 +70,8 @@
|
||||
multiSelectMode = false,
|
||||
selectedTables = $bindable([]),
|
||||
disabledTables = [],
|
||||
features
|
||||
features,
|
||||
asset
|
||||
}: Props = $props()
|
||||
|
||||
// Helper to check if a table is selected in multi-select mode
|
||||
@@ -442,16 +446,26 @@
|
||||
{:else}
|
||||
<!-- Normal mode: show tables for selected schema -->
|
||||
{#each filteredTableKeys as tableKey}
|
||||
<!-- PLACEHOLDER -->
|
||||
<button
|
||||
class={'w-full text-sm font-normal flex gap-2 items-center h-10 cursor-pointer pl-3 pr-1 ' +
|
||||
(selected.tableKey === tableKey ? 'bg-gray-500/25' : 'hover:bg-gray-500/10')}
|
||||
(selected.tableKey === tableKey ? 'bg-surface-secondary' : 'hover:bg-surface-hover')}
|
||||
onclick={() => (selected.tableKey = tableKey)}
|
||||
>
|
||||
<Table2 class="text-primary shrink-0" size={16} />
|
||||
{#if asset}
|
||||
<Star
|
||||
kind="asset"
|
||||
path={`${asset.kind}://${asset.path == 'main' ? '' : asset.path}/${selected.schemaKey}.${tableKey}`}
|
||||
/>
|
||||
{:else}
|
||||
<Table2 class="text-primary shrink-0" size={14} />
|
||||
{/if}
|
||||
|
||||
<p
|
||||
class="db-manager-table-key truncate text-ellipsis grow text-left text-emphasis text-xs"
|
||||
>{tableKey}</p
|
||||
>
|
||||
{tableKey}
|
||||
</p>
|
||||
<DropdownV2
|
||||
items={() => [
|
||||
{
|
||||
|
||||
@@ -164,6 +164,11 @@
|
||||
})}
|
||||
initialTableKey={input.specificTable}
|
||||
initialSchemaKey={input.type == 'database' ? input.specificSchema : undefined}
|
||||
asset={_input.type == 'ducklake'
|
||||
? { kind: 'ducklake', path: _input.ducklake }
|
||||
: _input.resourcePath.startsWith('datatable://')
|
||||
? { kind: 'datatable', path: _input.resourcePath.substring('datatable://'.length) }
|
||||
: undefined}
|
||||
{dbType}
|
||||
refresh={() => refresh()}
|
||||
{dbSelector}
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
)
|
||||
|
||||
export function openDrawer(nInput: DbInput) {
|
||||
console.log('Opening DB Manager with input:', nInput)
|
||||
input = nInput
|
||||
if (isDatatableInput) {
|
||||
datatables.refetch()
|
||||
@@ -73,6 +74,8 @@
|
||||
input = undefined
|
||||
selectedDatatable = undefined
|
||||
dbManagerContent?.clearReplResult()
|
||||
if (window.location.hash.startsWith('#dbmanager:'))
|
||||
history.replaceState('', document.title, window.location.href.replace(/#dbmanager:.*$/, ''))
|
||||
}
|
||||
|
||||
let windowWidth = $state(window.innerWidth)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { WorkspaceService } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
|
||||
|
||||
import Select from './select/Select.svelte'
|
||||
import DbManagerDrawer from './DBManagerDrawer.svelte'
|
||||
import ExploreAssetButton, { assetCanBeExplored } from './ExploreAssetButton.svelte'
|
||||
import { usePromise } from '$lib/svelte5Utils.svelte'
|
||||
|
||||
@@ -32,7 +31,7 @@
|
||||
let ducklakes = usePromise(() =>
|
||||
WorkspaceService.listDucklakes({ workspace: $workspaceStore ?? '' })
|
||||
)
|
||||
let dbManagerDrawer: DbManagerDrawer | undefined = $state()
|
||||
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
|
||||
</script>
|
||||
|
||||
<div class={className}>
|
||||
@@ -53,6 +52,4 @@
|
||||
{dbManagerDrawer}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<DbManagerDrawer bind:this={dbManagerDrawer} />
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { ResourceService } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
|
||||
import { onMount, untrack } from 'svelte'
|
||||
import AppConnect from './AppConnectDrawer.svelte'
|
||||
import ResourceEditorDrawer from './ResourceEditorDrawer.svelte'
|
||||
@@ -9,7 +9,6 @@
|
||||
import { Loader2, Pen, Plus, RotateCw } from 'lucide-svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import Select from './select/Select.svelte'
|
||||
import DbManagerDrawer from './DBManagerDrawer.svelte'
|
||||
import ExploreAssetButton, { assetCanBeExplored } from './ExploreAssetButton.svelte'
|
||||
import DropdownV2 from './DropdownV2.svelte'
|
||||
|
||||
@@ -156,7 +155,7 @@
|
||||
|
||||
let appConnect: AppConnect | undefined = $state()
|
||||
let resourceEditor: ResourceEditorDrawer | undefined = $state()
|
||||
let dbManagerDrawer: DbManagerDrawer | undefined = $state()
|
||||
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
|
||||
let hovering = $state(false)
|
||||
</script>
|
||||
|
||||
@@ -300,5 +299,3 @@
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<DbManagerDrawer bind:this={dbManagerDrawer} />
|
||||
|
||||
@@ -1,41 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy'
|
||||
|
||||
import { FavoriteService } from '$lib/gen'
|
||||
import { starStore } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { Star, StarOff } from 'lucide-svelte'
|
||||
import { favoriteManager, type FavoriteKind } from './sidebar/FavoriteMenu.svelte'
|
||||
|
||||
interface Props {
|
||||
path: string
|
||||
kind: 'flow' | 'app' | 'script' | 'raw_app'
|
||||
starred?: boolean
|
||||
workspace_id: string
|
||||
onStarred?: (starred: boolean) => void
|
||||
kind: FavoriteKind
|
||||
summary?: string
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
let { path, kind, starred = false, workspace_id, onStarred }: Props = $props()
|
||||
let { path, kind, workspaceId, summary }: Props = $props()
|
||||
|
||||
let buttonHover = $state(false)
|
||||
let starred = $derived(favoriteManager.isStarred(path, kind))
|
||||
|
||||
async function onClick() {
|
||||
buttonHover = false
|
||||
if (starred) {
|
||||
await FavoriteService.unstar({
|
||||
workspace: workspace_id,
|
||||
requestBody: { path, favorite_kind: kind }
|
||||
})
|
||||
sendUserToast('Unstarred')
|
||||
$starStore = $starStore + 1
|
||||
} else {
|
||||
await FavoriteService.star({
|
||||
workspace: workspace_id,
|
||||
requestBody: { path, favorite_kind: kind }
|
||||
})
|
||||
sendUserToast('Marked as favorite, it will appear first')
|
||||
$starStore = $starStore + 1
|
||||
}
|
||||
onStarred?.(!starred)
|
||||
if (starred) favoriteManager.unstar(path, kind, workspaceId)
|
||||
else favoriteManager.star(path, kind, workspaceId, summary)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -43,7 +27,7 @@
|
||||
onclick={preventDefault(onClick)}
|
||||
onmouseenter={() => (buttonHover = true)}
|
||||
onmouseleave={() => (buttonHover = false)}
|
||||
class="p-2"
|
||||
class="p-1"
|
||||
>
|
||||
{#if starred}
|
||||
{#if buttonHover}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
--ag-input-disabled-border-color: rgba(186, 191, 199, 0.3);
|
||||
--ag-input-disabled-background-color: rgba(186, 191, 199, 0.15);
|
||||
--ag-borders: solid 1px;
|
||||
--ag-border-radius: 8px;
|
||||
--ag-border-radius: 4px;
|
||||
--ag-borders-side-button: none;
|
||||
--ag-side-button-selected-background-color: transparent;
|
||||
--ag-header-column-resize-handle-display: block;
|
||||
@@ -66,8 +66,9 @@
|
||||
--ag-widget-vertical-spacing: calc(var(--ag-grid-size) * 1.5);
|
||||
--ag-toggle-button-height: 18px;
|
||||
--ag-toggle-button-width: 28px;
|
||||
--ag-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu,
|
||||
Cantarell, 'Helvetica Neue', sans-serif;
|
||||
--ag-font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
|
||||
'Helvetica Neue', sans-serif;
|
||||
--ag-font-size: 0.7rem;
|
||||
--ag-icon-font-family: agGridalpine;
|
||||
--ag-selected-tab-underline-color: var(--ag-alpine-active-color);
|
||||
@@ -92,8 +93,8 @@
|
||||
--ag-control-panel-background-color: #222628;
|
||||
--ag-subheader-background-color: #000;
|
||||
--ag-input-disabled-background-color: #282c2f;
|
||||
--ag-input-focus-box-shadow: 0 0 2px 0.5px rgba(255, 255, 255, 0.5),
|
||||
0 0 4px 3px var(--ag-input-focus-border-color);
|
||||
--ag-input-focus-box-shadow:
|
||||
0 0 2px 0.5px rgba(255, 255, 255, 0.5), 0 0 4px 3px var(--ag-input-focus-border-color);
|
||||
--ag-card-shadow: 0 1px 20px 1px black;
|
||||
--ag-disabled-foreground-color: rgba(255, 255, 255, 0.5);
|
||||
--ag-chip-background-color: rgba(255, 255, 255, 0.07);
|
||||
@@ -119,8 +120,8 @@
|
||||
--ag-control-panel-background-color: #222628;
|
||||
--ag-subheader-background-color: #000;
|
||||
--ag-input-disabled-background-color: #282c2f;
|
||||
--ag-input-focus-box-shadow: 0 0 2px 0.5px rgba(255, 255, 255, 0.5),
|
||||
0 0 4px 3px var(--ag-input-focus-border-color);
|
||||
--ag-input-focus-box-shadow:
|
||||
0 0 2px 0.5px rgba(255, 255, 255, 0.5), 0 0 4px 3px var(--ag-input-focus-border-color);
|
||||
--ag-card-shadow: 0 1px 20px 1px black;
|
||||
--ag-disabled-foreground-color: rgba(255, 255, 255, 0.5);
|
||||
--ag-chip-background-color: rgba(255, 255, 255, 0.07);
|
||||
|
||||
@@ -12,10 +12,9 @@
|
||||
type Asset,
|
||||
type AssetWithAltAccessType
|
||||
} from './lib'
|
||||
import DbManagerDrawer from '../DBManagerDrawer.svelte'
|
||||
import { untrack } from 'svelte'
|
||||
import { ResourceService, WorkspaceService } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
|
||||
import Tooltip from '../meltComponents/Tooltip.svelte'
|
||||
import Tooltip2 from '../Tooltip.svelte'
|
||||
import ResourceEditorDrawer from '../ResourceEditorDrawer.svelte'
|
||||
@@ -49,7 +48,7 @@
|
||||
let blueBgDiv: HTMLDivElement | undefined = $state()
|
||||
|
||||
let s3FilePicker: S3FilePicker | undefined = $state()
|
||||
let dbManagerDrawer: DbManagerDrawer | undefined = $state()
|
||||
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
|
||||
let resourceEditorDrawer: ResourceEditorDrawer | undefined = $state()
|
||||
let isOpen = $state(false)
|
||||
let resourceDataCache: Record<string, string | undefined> = $state({})
|
||||
@@ -218,5 +217,4 @@
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
<S3FilePicker bind:this={s3FilePicker} readOnlyMode />
|
||||
<DbManagerDrawer bind:this={dbManagerDrawer} />
|
||||
<ResourceEditorDrawer bind:this={resourceEditorDrawer} />
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { ResourceService, type Job } from '$lib/gen'
|
||||
import { inferAssets } from '$lib/infer'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
|
||||
import { usePromise } from '$lib/svelte5Utils.svelte'
|
||||
import { pruneNullishArray, uniqueBy } from '$lib/utils'
|
||||
import DbManagerDrawer from '../DBManagerDrawer.svelte'
|
||||
import ResourceEditorDrawer from '../ResourceEditorDrawer.svelte'
|
||||
import S3FilePicker from '../S3FilePicker.svelte'
|
||||
import AssetButtons from './AssetButtons.svelte'
|
||||
@@ -65,7 +64,7 @@
|
||||
})
|
||||
|
||||
let s3FilePicker: S3FilePicker | undefined = $state()
|
||||
let dbManagerDrawer: DbManagerDrawer | undefined = $state()
|
||||
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
|
||||
let resourceEditorDrawer: ResourceEditorDrawer | undefined = $state()
|
||||
</script>
|
||||
|
||||
@@ -99,5 +98,4 @@
|
||||
{/if}
|
||||
|
||||
<S3FilePicker bind:this={s3FilePicker} readOnlyMode />
|
||||
<DbManagerDrawer bind:this={dbManagerDrawer} />
|
||||
<ResourceEditorDrawer bind:this={resourceEditorDrawer} />
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
interface Props {
|
||||
app: ListableApp & { has_draft?: boolean; draft_only?: boolean; canWrite: boolean }
|
||||
marked: string | undefined
|
||||
starred: boolean
|
||||
shareModal: ShareModal
|
||||
moveDrawer: MoveDrawer
|
||||
deploymentDrawer: DeployWorkspaceDrawer
|
||||
@@ -48,7 +47,6 @@
|
||||
let {
|
||||
app,
|
||||
marked,
|
||||
starred,
|
||||
shareModal,
|
||||
moveDrawer,
|
||||
deploymentDrawer,
|
||||
@@ -81,11 +79,6 @@
|
||||
path={app.path}
|
||||
summary={app.summary}
|
||||
workspaceId={app.workspace_id ?? $workspaceStore ?? ''}
|
||||
{starred}
|
||||
onStarred={(newStarred) => {
|
||||
starred = newStarred
|
||||
dispatch('change')
|
||||
}}
|
||||
canFavorite={!app.draft_only}
|
||||
{depth}
|
||||
>
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
interface Props {
|
||||
flow: Flow & { has_draft?: boolean; draft_only?: boolean; canWrite: boolean }
|
||||
marked: string | undefined
|
||||
starred: boolean
|
||||
shareModal: ShareModal
|
||||
moveDrawer: MoveDrawer
|
||||
deleteConfirmedCallback: (() => void) | undefined
|
||||
@@ -51,7 +50,6 @@
|
||||
let {
|
||||
flow,
|
||||
marked,
|
||||
starred,
|
||||
shareModal,
|
||||
moveDrawer,
|
||||
deleteConfirmedCallback = $bindable(),
|
||||
@@ -106,12 +104,7 @@
|
||||
{marked}
|
||||
path={flow.path}
|
||||
summary={flow.summary}
|
||||
{starred}
|
||||
{errorHandlerMuted}
|
||||
onStarred={(newStarred) => {
|
||||
starred = newStarred
|
||||
dispatch('change')
|
||||
}}
|
||||
canFavorite={!flow.draft_only}
|
||||
{depth}
|
||||
>
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
interface Props {
|
||||
app: ListableRawApp & { canWrite: boolean }
|
||||
marked: string | undefined
|
||||
starred: boolean
|
||||
shareModal: ShareModal
|
||||
moveDrawer: MoveDrawer
|
||||
deleteConfirmedCallback: (() => void) | undefined
|
||||
@@ -33,7 +32,6 @@
|
||||
let {
|
||||
app,
|
||||
marked,
|
||||
starred,
|
||||
shareModal,
|
||||
moveDrawer,
|
||||
deleteConfirmedCallback = $bindable(),
|
||||
@@ -75,11 +73,6 @@
|
||||
path={app.path}
|
||||
summary={app.summary}
|
||||
workspaceId={app.workspace_id ?? $workspaceStore ?? ''}
|
||||
{starred}
|
||||
onStarred={(newStarred) => {
|
||||
starred = newStarred
|
||||
dispatch('change')
|
||||
}}
|
||||
canFavorite={true}
|
||||
{depth}
|
||||
>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
interface Props {
|
||||
marked: string | undefined
|
||||
starred: boolean
|
||||
selected?: boolean
|
||||
disabled?: boolean
|
||||
canFavorite?: boolean
|
||||
@@ -31,12 +30,10 @@
|
||||
currentTarget: EventTarget & HTMLInputElement
|
||||
}
|
||||
) => void
|
||||
onStarred?: (starred: boolean) => void
|
||||
}
|
||||
|
||||
let {
|
||||
marked,
|
||||
starred,
|
||||
selected = false,
|
||||
disabled = false,
|
||||
canFavorite = true,
|
||||
@@ -54,8 +51,7 @@
|
||||
badges,
|
||||
actions,
|
||||
customSummary,
|
||||
onSelect = () => {},
|
||||
onStarred = (newStarred: boolean) => {}
|
||||
onSelect = () => {}
|
||||
}: Props = $props()
|
||||
|
||||
let displayPath: string = (depth === 0 ? path : path?.split('/')?.slice(-1)?.[0]) ?? ''
|
||||
@@ -111,7 +107,7 @@
|
||||
|
||||
{#if canFavorite && kind !== 'resource' && kind !== 'variable' && kind !== 'resource_type'}
|
||||
<div class="center-center h-full text-xs font-semibold text-secondary w-9">
|
||||
<Star {kind} {path} {starred} workspace_id={workspaceId} {onStarred} />
|
||||
<Star {kind} {path} {workspaceId} {summary} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-9"></div>
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
interface Props {
|
||||
script: Script & { canWrite: boolean; use_codebase: boolean }
|
||||
marked: string | undefined
|
||||
starred: boolean
|
||||
shareModal: ShareModal
|
||||
moveDrawer: MoveDrawer
|
||||
deploymentDrawer: DeployWorkspaceDrawer
|
||||
@@ -61,7 +60,6 @@
|
||||
let {
|
||||
script,
|
||||
marked,
|
||||
starred,
|
||||
shareModal,
|
||||
moveDrawer,
|
||||
deploymentDrawer,
|
||||
@@ -119,13 +117,8 @@
|
||||
{marked}
|
||||
path={script.path}
|
||||
summary={script.summary}
|
||||
{starred}
|
||||
{errorHandlerMuted}
|
||||
workspaceId={$workspaceStore ?? ''}
|
||||
onStarred={(newStarred) => {
|
||||
starred = newStarred
|
||||
dispatch('change')
|
||||
}}
|
||||
canFavorite={!script.draft_only}
|
||||
{depth}
|
||||
>
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
let s = $state({
|
||||
val: {
|
||||
selectedAsset: undefined,
|
||||
dbManagerDrawer: undefined,
|
||||
s3FilePicker: undefined,
|
||||
resourceEditorDrawer: undefined,
|
||||
resourceMetadataCache: {},
|
||||
@@ -45,7 +44,6 @@
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import S3FilePicker from '../S3FilePicker.svelte'
|
||||
import DbManagerDrawer from '../DBManagerDrawer.svelte'
|
||||
import ResourceEditorDrawer from '../ResourceEditorDrawer.svelte'
|
||||
import { watch } from 'runed'
|
||||
|
||||
@@ -176,6 +174,5 @@
|
||||
|
||||
{#if flowGraphAssetsCtx}
|
||||
<S3FilePicker bind:this={flowGraphAssetsCtx.val.s3FilePicker} readOnlyMode />
|
||||
<DbManagerDrawer bind:this={flowGraphAssetsCtx.val.dbManagerDrawer} />
|
||||
<ResourceEditorDrawer bind:this={flowGraphAssetsCtx.val.resourceEditorDrawer} />
|
||||
{/if}
|
||||
|
||||
@@ -11,7 +11,6 @@ import type { StateStore } from '$lib/utils'
|
||||
import type { StepsInputArgs } from './stepsInputArgs.svelte'
|
||||
import type { Asset, AssetWithAccessType } from '../assets/lib'
|
||||
import type S3FilePicker from '../S3FilePicker.svelte'
|
||||
import type DbManagerDrawer from '../DBManagerDrawer.svelte'
|
||||
import type ResourceEditorDrawer from '../ResourceEditorDrawer.svelte'
|
||||
import type { ModulesTestStates } from '../modulesTest.svelte'
|
||||
import type { ButtonProp } from '$lib/components/DiffEditor.svelte'
|
||||
@@ -99,7 +98,6 @@ export type FlowEditorContext = {
|
||||
export type FlowGraphAssetContext = StateStore<{
|
||||
selectedAsset: Asset | undefined
|
||||
s3FilePicker: S3FilePicker | undefined
|
||||
dbManagerDrawer: DbManagerDrawer | undefined
|
||||
resourceEditorDrawer: ResourceEditorDrawer | undefined
|
||||
// Maps resource paths to their metadata. undefined is for error
|
||||
resourceMetadataCache: Record<string, { resource_type?: string } | undefined>
|
||||
|
||||
@@ -234,7 +234,7 @@
|
||||
import type { Edge, Node } from '@xyflow/svelte'
|
||||
|
||||
import { getNodeColorClasses, NODE } from '../../util'
|
||||
import { userStore } from '$lib/stores'
|
||||
import { globalDbManagerDrawer, userStore } from '$lib/stores'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { slide } from 'svelte/transition'
|
||||
import AssetColumnBadges from '$lib/components/assets/AssetColumnBadges.svelte'
|
||||
@@ -311,7 +311,7 @@
|
||||
noText
|
||||
buttonVariant="accent"
|
||||
s3FilePicker={flowGraphAssetsCtx?.val.s3FilePicker}
|
||||
dbManagerDrawer={flowGraphAssetsCtx?.val.dbManagerDrawer}
|
||||
dbManagerDrawer={globalDbManagerDrawer.val}
|
||||
_resourceMetadata={cachedResourceMetadata}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
{#if item.type == 'script'}
|
||||
<ScriptRow
|
||||
bind:deleteConfirmedCallback
|
||||
starred={item.starred ?? false}
|
||||
marked={item.marked}
|
||||
on:change={() => dispatch('scriptChanged')}
|
||||
script={item}
|
||||
@@ -46,7 +45,6 @@
|
||||
{:else if item.type == 'flow'}
|
||||
<FlowRow
|
||||
bind:deleteConfirmedCallback
|
||||
starred={item.starred ?? false}
|
||||
marked={item.marked}
|
||||
on:change={() => dispatch('flowChanged')}
|
||||
flow={item}
|
||||
@@ -63,7 +61,6 @@
|
||||
{:else if item.type == 'app'}
|
||||
<AppRow
|
||||
bind:deleteConfirmedCallback
|
||||
starred={item.starred ?? false}
|
||||
marked={item.marked}
|
||||
on:change={() => dispatch('appChanged')}
|
||||
app={item}
|
||||
@@ -76,7 +73,6 @@
|
||||
{:else if item.type == 'raw_app'}
|
||||
<RawAppRow
|
||||
bind:deleteConfirmedCallback
|
||||
starred={item.starred ?? false}
|
||||
marked={item.marked}
|
||||
on:change={() => dispatch('rawAppChanged')}
|
||||
app={item}
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
{target}
|
||||
onfocusin={onFocusIn}
|
||||
onfocusout={onFocusOut}
|
||||
onclick={onClick}
|
||||
>
|
||||
{@render children?.()}
|
||||
</a>
|
||||
|
||||
@@ -1,27 +1,100 @@
|
||||
<script module lang="ts">
|
||||
export type FavoriteKind = Exclude<
|
||||
NonNullable<StarData['requestBody']>['favorite_kind'],
|
||||
undefined
|
||||
>
|
||||
|
||||
export function getFavoriteHref(path: string, kind: FavoriteKind) {
|
||||
return {
|
||||
script: `/scripts/get/${path}`,
|
||||
flow: `/flows/get/${path}`,
|
||||
app: `/apps/get/${path}`,
|
||||
raw_app: `/apps_raw/get/${path}`,
|
||||
asset: `#dbmanager:${path}`
|
||||
}[kind]
|
||||
}
|
||||
export function getFavoriteLabel(path: string, kind: FavoriteKind): string {
|
||||
if (kind === 'asset') {
|
||||
const asset = parseFavoriteAsset(path)
|
||||
return `${asset.schema ? `${asset.schema}.` : ''}${asset.table} (${asset.assetName} ${asset.scheme})`
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
class FavoriteManager {
|
||||
current: {
|
||||
label: string
|
||||
path: string
|
||||
href: string
|
||||
kind: FavoriteKind
|
||||
}[] = $state([])
|
||||
|
||||
async unstar(path: string, favorite_kind: FavoriteKind, workspaceId?: string) {
|
||||
this.current = this.current.filter(
|
||||
(fav) => !(fav.path === path && fav.kind === favorite_kind)
|
||||
)
|
||||
|
||||
await FavoriteService.unstar({
|
||||
workspace: workspaceId ?? get(workspaceStore)!,
|
||||
requestBody: { path, favorite_kind }
|
||||
})
|
||||
}
|
||||
|
||||
async star(path: string, favorite_kind: FavoriteKind, workspaceId?: string, summary?: string) {
|
||||
const href = getFavoriteHref(path, favorite_kind)
|
||||
const label = summary || getFavoriteLabel(path, favorite_kind)
|
||||
this.current = [...this.current, { href, kind: favorite_kind, label, path }]
|
||||
await FavoriteService.star({
|
||||
workspace: workspaceId ?? get(workspaceStore)!,
|
||||
requestBody: { path, favorite_kind }
|
||||
})
|
||||
}
|
||||
|
||||
isStarred(path: string, favorite_kind: string) {
|
||||
return this.current.some((fav) => fav.path === path && fav.kind === favorite_kind)
|
||||
}
|
||||
}
|
||||
|
||||
export const favoriteManager = new FavoriteManager()
|
||||
|
||||
export function parseFavoriteAsset(path: string) {
|
||||
const [scheme, t0] = path.split('://')
|
||||
const [assetName, tableKey] = t0.split('/')
|
||||
const [t1, t2] = tableKey.split('.')
|
||||
const [table, schema] = [
|
||||
t2 ?? t1,
|
||||
t1 === 'main' || t1 === 'public' ? undefined : t2 ? t1 : undefined
|
||||
]
|
||||
return { table, schema, assetName: assetName || 'main', path, scheme }
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { CodeXml, LayoutDashboard, Star } from 'lucide-svelte'
|
||||
import { CodeXml, LayoutDashboard, Table2, Star, Trash2 } from 'lucide-svelte'
|
||||
import BarsStaggered from '$lib/components/icons/BarsStaggered.svelte'
|
||||
import { Menu, MenuItem } from '$lib/components/meltComponents'
|
||||
import MenuButton from '$lib/components/sidebar/MenuButton.svelte'
|
||||
import type { MenubarBuilders } from '@melt-ui/svelte'
|
||||
import { FavoriteService, type StarData } from '$lib/gen'
|
||||
import { get } from 'svelte/store'
|
||||
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
|
||||
import { parseDbInputFromAssetSyntax } from '$lib/utils'
|
||||
import DropdownV2 from '$lib/components/DropdownV2.svelte'
|
||||
|
||||
interface Props {
|
||||
lightMode?: boolean
|
||||
isCollapsed?: boolean
|
||||
favoriteLinks?: any
|
||||
favoriteLinks?: {
|
||||
label: string
|
||||
href: string
|
||||
path: string
|
||||
kind: FavoriteKind
|
||||
}[]
|
||||
createMenu: MenubarBuilders['createMenu']
|
||||
}
|
||||
|
||||
let {
|
||||
lightMode = false,
|
||||
isCollapsed = false,
|
||||
favoriteLinks = [] as {
|
||||
label: string
|
||||
href: string
|
||||
kind: 'script' | 'flow' | 'app' | 'raw_app'
|
||||
}[],
|
||||
createMenu
|
||||
}: Props = $props()
|
||||
let { lightMode = false, isCollapsed = false, favoriteLinks = [], createMenu }: Props = $props()
|
||||
</script>
|
||||
|
||||
<Menu {createMenu} usePointerDownOutside>
|
||||
@@ -46,25 +119,50 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="py-1 w-full max-w-full">
|
||||
{#each favoriteLinks ?? [] as favorite (favorite.href)}
|
||||
<MenuItem
|
||||
href={favorite.href}
|
||||
{item}
|
||||
class="w-full inline-flex flex-row px-4 py-2 data-[highlighted]:bg-surface-hover"
|
||||
>
|
||||
<span class="center-center">
|
||||
{#if favorite.kind == 'script'}
|
||||
<CodeXml size={16} />
|
||||
{:else if favorite.kind == 'flow'}
|
||||
<BarsStaggered size={16} />
|
||||
{:else if favorite.kind == 'app' || favorite.kind == 'raw_app'}
|
||||
<LayoutDashboard size={16} />
|
||||
{/if}
|
||||
</span>
|
||||
<span class="text-primary ml-2 grow min-w-0 text-xs truncate">
|
||||
{favorite.label}
|
||||
</span>
|
||||
</MenuItem>
|
||||
{#each favoriteLinks ?? [] as favorite}
|
||||
<div class="relative group">
|
||||
<MenuItem
|
||||
href={favorite.href}
|
||||
onClick={() => {
|
||||
if (favorite.kind === 'asset') {
|
||||
const dbInput = parseDbInputFromAssetSyntax(favorite.path)
|
||||
if (dbInput) globalDbManagerDrawer.val?.openDrawer(dbInput)
|
||||
}
|
||||
}}
|
||||
{item}
|
||||
class="w-full inline-flex flex-row px-4 py-2 pr-8 data-[highlighted]:bg-surface-hover"
|
||||
>
|
||||
<span class="center-center">
|
||||
{#if favorite.kind == 'script'}
|
||||
<CodeXml size={16} />
|
||||
{:else if favorite.kind == 'flow'}
|
||||
<BarsStaggered size={16} />
|
||||
{:else if favorite.kind == 'app' || favorite.kind == 'raw_app'}
|
||||
<LayoutDashboard size={16} />
|
||||
{:else if favorite.kind == 'asset'}
|
||||
<Table2 size={16} />
|
||||
{/if}
|
||||
</span>
|
||||
<span class="text-primary ml-2 grow min-w-0 text-xs truncate">
|
||||
{favorite.label}
|
||||
</span>
|
||||
</MenuItem>
|
||||
<div
|
||||
class="absolute right-0 top-0 h-full flex items-center pr-1 opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<DropdownV2
|
||||
items={[
|
||||
{
|
||||
displayName: 'Delete',
|
||||
icon: Trash2,
|
||||
action: () => favoriteManager.unstar(favorite.path, favorite.kind)
|
||||
}
|
||||
]}
|
||||
size="xs"
|
||||
fixedHeight={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
Building,
|
||||
Calendar,
|
||||
ServerCog,
|
||||
GraduationCap
|
||||
GraduationCap,
|
||||
Table2
|
||||
} from 'lucide-svelte'
|
||||
import { base } from '$lib/base'
|
||||
|
||||
@@ -35,21 +36,19 @@
|
||||
import MenuButton, { sidebarClasses } from './MenuButton.svelte'
|
||||
import MenuLink from './MenuLink.svelte'
|
||||
import ResizeTransitionWrapper from '../common/ResizeTransitionWrapper.svelte'
|
||||
import type { FavoriteKind } from './FavoriteMenu.svelte'
|
||||
let darkMode: boolean = $state(false)
|
||||
|
||||
interface Props {
|
||||
isCollapsed?: boolean
|
||||
favoriteLinks?: any
|
||||
}
|
||||
|
||||
let {
|
||||
isCollapsed = false,
|
||||
favoriteLinks = [] as {
|
||||
favoriteLinks?: {
|
||||
label: string
|
||||
href: string
|
||||
kind: 'script' | 'flow' | 'app' | 'raw_app'
|
||||
kind: FavoriteKind
|
||||
}[]
|
||||
}: Props = $props()
|
||||
}
|
||||
|
||||
let { isCollapsed = false, favoriteLinks = [] }: Props = $props()
|
||||
|
||||
let mainMenuLinks = $derived(
|
||||
[
|
||||
@@ -206,6 +205,8 @@
|
||||
<BarsStaggered size={16} />
|
||||
{:else if favorite.kind == 'app' || favorite.kind == 'raw_app'}
|
||||
<LayoutDashboard size={16} />
|
||||
{:else if favorite.kind == 'asset'}
|
||||
<Table2 size={16} />
|
||||
{/if}
|
||||
</span>
|
||||
<span class="text-primary ml-2 grow min-w-0 text-xs truncate">
|
||||
|
||||
@@ -62,12 +62,11 @@
|
||||
import { random_adj } from '../random_positive_adjetive'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { SettingService, WorkspaceService, type GetSettingsResponse } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
|
||||
import { createAsyncConfirmationModal } from '../common/confirmationModal/asyncConfirmationModal.svelte'
|
||||
import ConfirmationModal from '../common/confirmationModal/ConfirmationModal.svelte'
|
||||
import { resource } from 'runed'
|
||||
import CustomInstanceDbSelect from './CustomInstanceDbSelect.svelte'
|
||||
import DBManagerDrawer from '../DBManagerDrawer.svelte'
|
||||
import { Popover } from '../meltComponents'
|
||||
import ExploreAssetButton from '../ExploreAssetButton.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
@@ -140,7 +139,7 @@
|
||||
}
|
||||
|
||||
let confirmationModal = createAsyncConfirmationModal()
|
||||
let dbManagerDrawer: DBManagerDrawer | undefined = $state()
|
||||
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
|
||||
let dirtyMap = $derived.by(() => {
|
||||
const map: Record<string, boolean> = {}
|
||||
for (let i = 0; i < tempSettings.dataTables.length; i++) {
|
||||
@@ -290,4 +289,3 @@
|
||||
<Button wrapperClasses="mt-4 mb-16 max-w-fit" on:click={onSave} variant="accent">Save</Button>
|
||||
|
||||
<ConfirmationModal {...confirmationModal.props} />
|
||||
<DBManagerDrawer bind:this={dbManagerDrawer} />
|
||||
|
||||
@@ -67,10 +67,9 @@
|
||||
import { SettingService, WorkspaceService } from '$lib/gen'
|
||||
import type { GetSettingsResponse } from '$lib/gen'
|
||||
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import ExploreAssetButton from '../ExploreAssetButton.svelte'
|
||||
import DbManagerDrawer from '../DBManagerDrawer.svelte'
|
||||
import Tooltip from '../Tooltip.svelte'
|
||||
import ConfirmationModal from '../common/confirmationModal/ConfirmationModal.svelte'
|
||||
import { createAsyncConfirmationModal } from '../common/confirmationModal/asyncConfirmationModal.svelte'
|
||||
@@ -179,7 +178,7 @@
|
||||
'Where the data is actually stored, in parquet format. You need to configure a workspace storage first'
|
||||
}
|
||||
|
||||
let dbManagerDrawer: DbManagerDrawer | undefined = $state()
|
||||
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
|
||||
let confirmationModal = createAsyncConfirmationModal()
|
||||
</script>
|
||||
|
||||
@@ -394,6 +393,5 @@
|
||||
>
|
||||
Save ducklake settings
|
||||
</Button>
|
||||
<DbManagerDrawer bind:this={dbManagerDrawer} />
|
||||
|
||||
<ConfirmationModal {...confirmationModal.props} />
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { getLocalSetting, type StateStore } from './utils'
|
||||
import { createState } from './svelte5Utils.svelte'
|
||||
import { DEFAULT_HUB_BASE_URL } from './hub'
|
||||
import type DBManagerDrawer from './components/DBManagerDrawer.svelte'
|
||||
|
||||
export interface UserExt {
|
||||
email: string
|
||||
@@ -77,7 +78,6 @@ export const workspaceStore = writable<string | undefined>(
|
||||
export const defaultScripts = writable<WorkspaceDefaultScripts | undefined>(undefined)
|
||||
export const dbClockDrift = writable<number | undefined>(undefined)
|
||||
export const isPremiumStore = writable<boolean>(false)
|
||||
export const starStore = writable(1)
|
||||
export const usersWorkspaceStore = writable<UserWorkspaceList | undefined>(undefined)
|
||||
export const superadmin = writable<string | false | undefined>(undefined)
|
||||
export const devopsRole = writable<string | false | undefined>(undefined)
|
||||
@@ -127,6 +127,10 @@ export const codeCompletionSessionEnabled = writable<boolean>(
|
||||
|
||||
export const usedTriggerKinds = writable<string[]>([])
|
||||
|
||||
export let globalDbManagerDrawer: StateStore<DBManagerDrawer | undefined> = createState({
|
||||
val: undefined
|
||||
})
|
||||
|
||||
type SQLBaseSchema = {
|
||||
[schemaKey: string]: {
|
||||
[tableKey: string]: {
|
||||
|
||||
@@ -1783,6 +1783,7 @@ import { darkModeName, lightModeName } from './assets/tokens/colorTokensConfig'
|
||||
import BarsStaggered from './components/icons/BarsStaggered.svelte'
|
||||
import { GitIcon } from './components/icons'
|
||||
import { Bot, Code, Package } from 'lucide-svelte'
|
||||
import type { DbInput } from './components/dbTypes'
|
||||
export function getCssColor(
|
||||
color: CssColor,
|
||||
{
|
||||
@@ -1986,6 +1987,23 @@ export function pick<T extends object, K extends keyof T>(obj: T, keys: readonly
|
||||
return result
|
||||
}
|
||||
|
||||
export function parseDbInputFromAssetSyntax(path: string): DbInput | null {
|
||||
const [p1, _p2] = path.split('://')
|
||||
const [p2, _p3] = _p2.split('/')
|
||||
const [p3, p4] = _p3.split('.')
|
||||
return p1 === 'ducklake'
|
||||
? { type: 'ducklake', ducklake: p2 || 'main', specificTable: p4 ?? p3 }
|
||||
: p1 === 'datatable'
|
||||
? {
|
||||
type: 'database',
|
||||
resourcePath: `datatable://${p2 || 'main'}`,
|
||||
resourceType: 'postgresql',
|
||||
specificTable: p4 ?? p3,
|
||||
specificSchema: p4 ? p3 : undefined
|
||||
}
|
||||
: null
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats memory size in KB to human-readable format with appropriate units
|
||||
* @param sizeInKb - Memory size in kilobytes
|
||||
@@ -1993,11 +2011,17 @@ export function pick<T extends object, K extends keyof T>(obj: T, keys: readonly
|
||||
* @returns Formatted string with appropriate unit (KB, MB, GB) or object with display and tooltip
|
||||
*/
|
||||
export function formatMemory(sizeInKb: number): string
|
||||
export function formatMemory(sizeInKb: number, includeTooltip: true): { display: string; tooltip: string }
|
||||
export function formatMemory(sizeInKb: number, includeTooltip = false): string | { display: string; tooltip: string } {
|
||||
const precise = `${sizeInKb.toLocaleString()}KB`;
|
||||
export function formatMemory(
|
||||
sizeInKb: number,
|
||||
includeTooltip: true
|
||||
): { display: string; tooltip: string }
|
||||
export function formatMemory(
|
||||
sizeInKb: number,
|
||||
includeTooltip = false
|
||||
): string | { display: string; tooltip: string } {
|
||||
const precise = `${sizeInKb.toLocaleString()}KB`
|
||||
|
||||
let display: string;
|
||||
let display: string
|
||||
if (sizeInKb >= 1024 * 1024) {
|
||||
// Convert to GB for values >= 1GB
|
||||
display = `${(sizeInKb / (1024 * 1024)).toFixed(0)}GB`
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import {
|
||||
AppService,
|
||||
AssetService,
|
||||
FlowService,
|
||||
OpenAPI,
|
||||
RawAppService,
|
||||
@@ -11,14 +12,19 @@
|
||||
UserService,
|
||||
WorkspaceService
|
||||
} from '$lib/gen'
|
||||
import { capitalize, classNames, getModifierKey, sendUserToast } from '$lib/utils'
|
||||
import {
|
||||
capitalize,
|
||||
classNames,
|
||||
getModifierKey,
|
||||
parseDbInputFromAssetSyntax,
|
||||
sendUserToast
|
||||
} from '$lib/utils'
|
||||
import WorkspaceMenu from '$lib/components/sidebar/WorkspaceMenu.svelte'
|
||||
import SidebarContent from '$lib/components/sidebar/SidebarContent.svelte'
|
||||
import CriticalAlertModal from '$lib/components/sidebar/CriticalAlertModal.svelte'
|
||||
import {
|
||||
enterpriseLicense,
|
||||
isPremiumStore,
|
||||
starStore,
|
||||
superadmin,
|
||||
usageStore,
|
||||
workspaceUsageStore,
|
||||
@@ -29,7 +35,8 @@
|
||||
hubBaseUrlStore,
|
||||
usedTriggerKinds,
|
||||
devopsRole,
|
||||
whitelabelNameStore
|
||||
whitelabelNameStore,
|
||||
globalDbManagerDrawer
|
||||
} from '$lib/stores'
|
||||
import CenteredModal from '$lib/components/CenteredModal.svelte'
|
||||
import { afterNavigate, beforeNavigate } from '$app/navigation'
|
||||
@@ -38,7 +45,11 @@
|
||||
import SuperadminSettings from '$lib/components/SuperadminSettings.svelte'
|
||||
import WindmillIcon from '$lib/components/icons/WindmillIcon.svelte'
|
||||
import { page } from '$app/stores'
|
||||
import FavoriteMenu from '$lib/components/sidebar/FavoriteMenu.svelte'
|
||||
import FavoriteMenu, {
|
||||
favoriteManager,
|
||||
getFavoriteHref,
|
||||
getFavoriteLabel
|
||||
} from '$lib/components/sidebar/FavoriteMenu.svelte'
|
||||
import { SUPERADMIN_SETTINGS_HASH, USER_SETTINGS_HASH } from '$lib/components/sidebar/settings'
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import { syncTutorialsTodos } from '$lib/tutorialUtils'
|
||||
@@ -54,6 +65,8 @@
|
||||
import { aiChatManager } from '$lib/components/copilot/chat/AIChatManager.svelte'
|
||||
import AiChatLayout from '$lib/components/copilot/chat/AiChatLayout.svelte'
|
||||
import { DEFAULT_HUB_BASE_URL } from '$lib/hub'
|
||||
import DBManagerDrawer from '$lib/components/DBManagerDrawer.svelte'
|
||||
import { watchOnce } from 'runed'
|
||||
interface Props {
|
||||
children?: import('svelte').Snippet
|
||||
}
|
||||
@@ -133,14 +146,6 @@
|
||||
|
||||
let innerWidth = $state(BROWSER ? window.innerWidth : 2000)
|
||||
|
||||
let favoriteLinks = $state(
|
||||
[] as {
|
||||
label: string
|
||||
href: string
|
||||
kind: 'app' | 'script' | 'flow' | 'raw_app'
|
||||
}[]
|
||||
)
|
||||
|
||||
function onLoad() {
|
||||
loadFavorites()
|
||||
loadUsage()
|
||||
@@ -185,26 +190,37 @@
|
||||
workspace: $workspaceStore ?? '',
|
||||
starredOnly: true
|
||||
})
|
||||
favoriteLinks = [
|
||||
const assets = await AssetService.listFavoriteAssets({ workspace: $workspaceStore ?? '' })
|
||||
favoriteManager.current = [
|
||||
...scripts.map((s) => ({
|
||||
label: s.summary || s.path,
|
||||
href: `${base}/scripts/get/${s.hash}`,
|
||||
kind: 'script' as 'script'
|
||||
label: s.summary || getFavoriteLabel(s.path, 'script'),
|
||||
path: s.path,
|
||||
href: getFavoriteHref(s.path, 'script'),
|
||||
kind: 'script' as const
|
||||
})),
|
||||
...flows.map((f) => ({
|
||||
label: f.summary || f.path,
|
||||
href: `${base}/flows/get/${f.path}`,
|
||||
kind: 'flow' as 'flow'
|
||||
label: f.summary || getFavoriteLabel(f.path, 'flow'),
|
||||
path: f.path,
|
||||
href: getFavoriteHref(f.path, 'flow'),
|
||||
kind: 'flow' as const
|
||||
})),
|
||||
...apps.map((f) => ({
|
||||
label: f.summary || f.path,
|
||||
href: `${base}/apps/get/${f.path}`,
|
||||
kind: 'app' as 'app'
|
||||
label: f.summary || getFavoriteLabel(f.path, 'app'),
|
||||
path: f.path,
|
||||
href: getFavoriteHref(f.path, 'app'),
|
||||
kind: 'app' as const
|
||||
})),
|
||||
...raw_apps.map((f) => ({
|
||||
label: f.summary || f.path,
|
||||
href: `${base}/apps/get_raw/${f.version}/${f.path}`,
|
||||
kind: 'raw_app' as 'raw_app'
|
||||
label: f.summary || getFavoriteLabel(f.path, 'raw_app'),
|
||||
path: f.path,
|
||||
href: getFavoriteHref(f.path, 'raw_app'),
|
||||
kind: 'raw_app' as const
|
||||
})),
|
||||
...assets.map((a) => ({
|
||||
label: getFavoriteLabel(a.path, 'asset'),
|
||||
path: a.path,
|
||||
href: getFavoriteHref(a.path, 'asset'),
|
||||
kind: 'asset' as const
|
||||
}))
|
||||
]
|
||||
}
|
||||
@@ -369,7 +385,7 @@
|
||||
untrack(() => updateUserStore($workspaceStore))
|
||||
})
|
||||
$effect(() => {
|
||||
$workspaceStore && $starStore && untrack(() => onLoad())
|
||||
$workspaceStore && untrack(() => onLoad())
|
||||
})
|
||||
$effect(() => {
|
||||
innerWidth && untrack(() => changeCollapsed())
|
||||
@@ -409,6 +425,19 @@
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
watchOnce(
|
||||
() => globalDbManagerDrawer.val,
|
||||
() => {
|
||||
if (!globalDbManagerDrawer.val) return
|
||||
const hash = window.location.hash
|
||||
if (hash.startsWith('#dbmanager:')) {
|
||||
const [_, path] = hash.split('#dbmanager:')
|
||||
const dbInput = parseDbInputFromAssetSyntax(path)
|
||||
if (dbInput) globalDbManagerDrawer.val?.openDrawer(dbInput)
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth />
|
||||
@@ -496,7 +525,7 @@
|
||||
<Menubar>
|
||||
{#snippet children({ createMenu })}
|
||||
<WorkspaceMenu {createMenu} />
|
||||
<FavoriteMenu {createMenu} {favoriteLinks} />
|
||||
<FavoriteMenu {createMenu} favoriteLinks={favoriteManager.current} />
|
||||
{/snippet}
|
||||
</Menubar>
|
||||
<MenuButton
|
||||
@@ -570,7 +599,11 @@
|
||||
<Menubar class="flex flex-col gap-1">
|
||||
{#snippet children({ createMenu })}
|
||||
<WorkspaceMenu {createMenu} {isCollapsed} />
|
||||
<FavoriteMenu {createMenu} {favoriteLinks} {isCollapsed} />
|
||||
<FavoriteMenu
|
||||
{createMenu}
|
||||
favoriteLinks={favoriteManager.current}
|
||||
{isCollapsed}
|
||||
/>
|
||||
{/snippet}
|
||||
</Menubar>
|
||||
<MenuButton
|
||||
@@ -624,7 +657,7 @@
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="absolute top-2 left-2 z5000">
|
||||
<OperatorMenu {favoriteLinks} />
|
||||
<OperatorMenu favoriteLinks={favoriteManager.current} />
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Legacy menu -->
|
||||
@@ -683,7 +716,7 @@
|
||||
<Menubar>
|
||||
{#snippet children({ createMenu })}
|
||||
<WorkspaceMenu {createMenu} />
|
||||
<FavoriteMenu {createMenu} {favoriteLinks} />
|
||||
<FavoriteMenu {createMenu} favoriteLinks={favoriteManager.current} />
|
||||
{/snippet}
|
||||
</Menubar>
|
||||
<MenuButton
|
||||
@@ -735,3 +768,7 @@
|
||||
{:else}
|
||||
<CenteredModal title="Loading user..." loading={true}></CenteredModal>
|
||||
{/if}
|
||||
|
||||
{#if $workspaceStore}
|
||||
<DBManagerDrawer bind:this={globalDbManagerDrawer.val} />
|
||||
{/if}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { formatAsset, formatAssetKind } from '$lib/components/assets/lib'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
import DbManagerDrawer from '$lib/components/DBManagerDrawer.svelte'
|
||||
import PageHeader from '$lib/components/PageHeader.svelte'
|
||||
import S3FilePicker from '$lib/components/S3FilePicker.svelte'
|
||||
import { Cell, DataTable } from '$lib/components/table'
|
||||
@@ -13,15 +12,15 @@
|
||||
type AssetKind,
|
||||
type ListAssetsResponse
|
||||
} from '$lib/gen'
|
||||
import { userStore, workspaceStore, userWorkspaces } from '$lib/stores'
|
||||
import { pluralize, truncate } from '$lib/utils'
|
||||
import { userStore, workspaceStore, userWorkspaces, globalDbManagerDrawer } from '$lib/stores'
|
||||
import { parseDbInputFromAssetSyntax, pluralize, truncate } from '$lib/utils'
|
||||
import ExploreAssetButton, {
|
||||
assetCanBeExplored
|
||||
} from '../../../../lib/components/ExploreAssetButton.svelte'
|
||||
import AssetsUsageDrawer from '$lib/components/assets/AssetsUsageDrawer.svelte'
|
||||
import AssetGenericIcon from '$lib/components/icons/AssetGenericIcon.svelte'
|
||||
import { Tooltip } from '$lib/components/meltComponents'
|
||||
import { AlertTriangle, Loader2, SettingsIcon } from 'lucide-svelte'
|
||||
import { AlertTriangle, Loader2, SettingsIcon, StarIcon } from 'lucide-svelte'
|
||||
import { StaleWhileLoading, useInfiniteQuery, useScrollToBottom } from '$lib/svelte5Utils.svelte'
|
||||
import { Debounced, resource, watch, type ResourceReturn } from 'runed'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
@@ -30,6 +29,7 @@
|
||||
import RefreshButton from '$lib/components/common/button/RefreshButton.svelte'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { favoriteManager, parseFavoriteAsset } from '$lib/components/sidebar/FavoriteMenu.svelte'
|
||||
|
||||
interface AssetCursor {
|
||||
created_at?: string
|
||||
@@ -82,7 +82,7 @@
|
||||
let assets = $derived(_assets.current?.flatMap((page) => page.assets))
|
||||
|
||||
let s3FilePicker: S3FilePicker | undefined = $state()
|
||||
let dbManagerDrawer: DbManagerDrawer | undefined = $state()
|
||||
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
|
||||
let assetsUsageDropdown: AssetsUsageDrawer | undefined = $state()
|
||||
|
||||
let allS3Storages = resource(
|
||||
@@ -101,16 +101,22 @@
|
||||
() => $workspaceStore,
|
||||
() =>
|
||||
WorkspaceService.listDucklakes({ workspace: $workspaceStore! }).then((d) =>
|
||||
d.map((d) => ({ label: d == 'main' ? 'Primary ducklake' : d, value: d }))
|
||||
d.map((d) => ({ label: d == 'main' ? 'Main ducklake' : d, value: d }))
|
||||
)
|
||||
)
|
||||
let allDataTables = resource(
|
||||
() => $workspaceStore,
|
||||
() =>
|
||||
WorkspaceService.listDataTables({ workspace: $workspaceStore! }).then((d) =>
|
||||
d.map((d) => ({ label: d == 'main' ? 'Primary data table' : d, value: d }))
|
||||
d.map((d) => ({ label: d == 'main' ? 'Main data table' : d, value: d }))
|
||||
)
|
||||
)
|
||||
|
||||
function extractFavorites(kind: AssetKind) {
|
||||
return favoriteManager.current
|
||||
.filter((f) => f.kind === 'asset' && f.path.startsWith(kind))
|
||||
.map((f) => parseFavoriteAsset(f.path))
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $userStore?.operator && $workspaceStore && !$userWorkspaces.find((_) => _.id === $workspaceStore)?.operator_settings?.assets}
|
||||
@@ -133,14 +139,36 @@
|
||||
assetKind: AssetKind
|
||||
data: ResourceReturn<{ label: string; value: string }[]>
|
||||
settingsHref: string
|
||||
docsHref: string
|
||||
favorites?: { table: string; schema?: string; assetName: string; path: string }[]
|
||||
})}
|
||||
<div class="flex flex-col bg-surface-tertiary drop-shadow-base rounded-md flex-1">
|
||||
<div class="flex justify-between px-6 pt-5 border-b">
|
||||
<h3 class="text-sm font-bold mb-4">{props.title}</h3>
|
||||
<a class="text-xs" href={props.settingsHref}>Settings </a>
|
||||
<div class="flex justify-between border-b">
|
||||
<h3 class="text-sm font-bold mb-4 pt-5 pl-6">{props.title}</h3>
|
||||
<div class="flex items-center h-fit gap-2 mt-4 mr-4">
|
||||
<Button
|
||||
wrapperClasses="h-fit"
|
||||
btnClasses="text-accent"
|
||||
variant="subtle"
|
||||
unifiedSize="sm"
|
||||
href={props.docsHref}
|
||||
target="_blank"
|
||||
>
|
||||
See documentation
|
||||
</Button>
|
||||
<Button
|
||||
wrapperClasses="h-fit"
|
||||
variant={props.data.current?.length === 0 && !props.data.loading
|
||||
? 'accent'
|
||||
: 'subtle'}
|
||||
iconOnly
|
||||
endIcon={{ icon: SettingsIcon }}
|
||||
href={props.settingsHref}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if props.data.current?.length}
|
||||
<div class="max-h-96 overflow-y-auto pb-3">
|
||||
<div class="max-h-96 overflow-y-auto pb-1">
|
||||
{#each props.data.current ?? [] as item}
|
||||
<div class="text-xs py-2 text-primary flex justify-between items-center px-6">
|
||||
{item.label}
|
||||
@@ -153,6 +181,34 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if !props.data.loading && !props.data.error && props.favorites != undefined}
|
||||
<div class="mb-4 pt-2 px-6">
|
||||
<h3 class="text-xs font-bold mb-1"> Favorite tables</h3>
|
||||
<div class="flex gap-1 flex-wrap">
|
||||
{#each props.favorites as fav}
|
||||
<button
|
||||
class="text-xs font-normal bg-surface-sunken rounded-md px-2 py-1 cursor-pointer hover:opacity-80"
|
||||
onclick={() => {
|
||||
const dbInput = parseDbInputFromAssetSyntax(fav.path)
|
||||
if (dbInput) globalDbManagerDrawer.val?.openDrawer(dbInput)
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<StarIcon size="12" class="inline mr-1" />
|
||||
{fav.schema ? `${fav.schema}.` : ''}{fav.table}
|
||||
</span>
|
||||
<span class="text-2xs">
|
||||
{fav.assetName === 'main' ? `` : `(${fav.assetName})`}
|
||||
</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{#if props.favorites.length === 0}
|
||||
<div class="text-xs text-secondary"> No favorite table yet</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if props.data.loading}
|
||||
<div class="flex items-center gap-2 mt-2 mb-5 px-6 text-sm text-secondary">
|
||||
@@ -163,37 +219,35 @@
|
||||
Error loading {props.title.toLowerCase()}
|
||||
</div>
|
||||
{:else if props.data.current?.length === 0}
|
||||
<div class="text-sm text-secondary mt-2 px-6 mb-3">
|
||||
<div class="text-xs text-secondary mt-2 px-6 mb-3">
|
||||
No {props.title.toLowerCase()} yet
|
||||
</div>
|
||||
<Button
|
||||
endIcon={{ icon: SettingsIcon }}
|
||||
href={props.settingsHref}
|
||||
variant="accent"
|
||||
wrapperClasses="w-fit px-6"
|
||||
>
|
||||
{props.title} settings
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
{@render card({
|
||||
title: 'Data tables',
|
||||
title: 'Data table',
|
||||
data: allDataTables,
|
||||
assetKind: 'datatable',
|
||||
settingsHref: '/workspace_settings?tab=windmill_data_tables'
|
||||
settingsHref: '/workspace_settings?tab=windmill_data_tables',
|
||||
docsHref: 'https://www.windmill.dev/docs/core_concepts/persistent_storage/data_tables',
|
||||
favorites: extractFavorites('datatable')
|
||||
})}
|
||||
{@render card({
|
||||
title: 'Ducklakes',
|
||||
title: 'Ducklake',
|
||||
data: allDucklakes,
|
||||
assetKind: 'ducklake',
|
||||
settingsHref: '/workspace_settings?tab=windmill_lfs'
|
||||
settingsHref: '/workspace_settings?tab=windmill_lfs',
|
||||
docsHref: 'https://www.windmill.dev/docs/core_concepts/persistent_storage/ducklake',
|
||||
favorites: extractFavorites('ducklake')
|
||||
})}
|
||||
{@render card({
|
||||
title: 'Workspace object storages',
|
||||
title: 'Object storage',
|
||||
data: allS3Storages,
|
||||
assetKind: 's3object',
|
||||
settingsHref: '/workspace_settings?tab=windmill_lfs'
|
||||
settingsHref: '/workspace_settings?tab=windmill_lfs',
|
||||
docsHref:
|
||||
'https://www.windmill.dev/docs/core_concepts/persistent_storage/large_data_files'
|
||||
})}
|
||||
</div>
|
||||
</Section>
|
||||
@@ -233,7 +287,6 @@
|
||||
|
||||
<AssetsUsageDrawer bind:this={assetsUsageDropdown} />
|
||||
<S3FilePicker bind:this={s3FilePicker} readOnlyMode />
|
||||
<DbManagerDrawer bind:this={dbManagerDrawer} />
|
||||
|
||||
{#snippet table()}
|
||||
<DataTable>
|
||||
|
||||
@@ -120,8 +120,6 @@
|
||||
goto('/')
|
||||
}
|
||||
|
||||
let starred: boolean | undefined = $state(undefined)
|
||||
|
||||
async function loadTriggersCount() {
|
||||
$triggersCount = await FlowService.getTriggersCountOfFlow({
|
||||
workspace: $workspaceStore!,
|
||||
@@ -146,7 +144,6 @@
|
||||
path,
|
||||
withStarredInfo: true
|
||||
})
|
||||
starred = flow.starred
|
||||
if (!flow.path.startsWith(`u/${$userStore?.username}`) && flow.path.split('/').length > 2) {
|
||||
invisible_to_owner = flow.visible_to_runner_only
|
||||
}
|
||||
@@ -512,15 +509,7 @@
|
||||
/>
|
||||
{/snippet}
|
||||
{#if $workspaceStore && flow}
|
||||
<Star
|
||||
kind="flow"
|
||||
path={flow.path}
|
||||
{starred}
|
||||
workspace_id={$workspaceStore}
|
||||
onStarred={(newStarred) => {
|
||||
starred = newStarred
|
||||
}}
|
||||
/>
|
||||
<Star kind="flow" path={flow.path} summary={flow.summary} />
|
||||
{/if}
|
||||
{#if flow?.value?.priority != undefined}
|
||||
<div class="hidden md:block">
|
||||
|
||||
@@ -29,7 +29,13 @@
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import type { ResourceType, WorkspaceDeployUISettings } from '$lib/gen'
|
||||
import { OauthService, ResourceService, WorkspaceService, type ListableResource } from '$lib/gen'
|
||||
import { enterpriseLicense, userStore, workspaceStore, userWorkspaces } from '$lib/stores'
|
||||
import {
|
||||
enterpriseLicense,
|
||||
userStore,
|
||||
workspaceStore,
|
||||
userWorkspaces,
|
||||
globalDbManagerDrawer
|
||||
} from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import {
|
||||
canWrite,
|
||||
@@ -65,7 +71,6 @@
|
||||
import ExploreAssetButton, {
|
||||
assetCanBeExplored
|
||||
} from '../../../../lib/components/ExploreAssetButton.svelte'
|
||||
import DbManagerDrawer from '$lib/components/DBManagerDrawer.svelte'
|
||||
|
||||
type ResourceW = ListableResource & { canWrite: boolean; marked?: string }
|
||||
type ResourceTypeW = ResourceType & { canWrite: boolean }
|
||||
@@ -473,7 +478,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
let dbManagerDrawer: DbManagerDrawer | undefined = $state()
|
||||
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
|
||||
|
||||
let filterUserFolders = $state(false)
|
||||
let filterUserFoldersType: 'only f/*' | 'u/username and f/*' | undefined = $derived(
|
||||
@@ -1170,7 +1175,6 @@
|
||||
<SupabaseConnect bind:this={supabaseConnect} on:refresh={loadResources} />
|
||||
<AppConnect bind:this={appConnect} on:refresh={loadResources} />
|
||||
<ResourceEditorDrawer bind:this={resourceEditor} on:refresh={loadResources} />
|
||||
<DbManagerDrawer bind:this={dbManagerDrawer} />
|
||||
|
||||
<ShareModal
|
||||
bind:this={shareModal}
|
||||
|
||||
@@ -168,7 +168,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
let starred: boolean | undefined = $state(undefined)
|
||||
|
||||
async function loadTriggers(path: string): Promise<void> {
|
||||
await triggersState.fetchTriggers(
|
||||
@@ -208,7 +207,6 @@
|
||||
no_main_func: false,
|
||||
has_preprocessor: false
|
||||
}
|
||||
starred = false
|
||||
can_write = false
|
||||
return
|
||||
} catch (e) {
|
||||
@@ -225,7 +223,6 @@
|
||||
withStarredInfo: true,
|
||||
authed: true
|
||||
})
|
||||
starred = script.starred
|
||||
} catch {
|
||||
try {
|
||||
script = await ScriptService.getScriptByPath({
|
||||
@@ -233,7 +230,6 @@
|
||||
path: hash,
|
||||
withStarredInfo: true
|
||||
})
|
||||
starred = script.starred
|
||||
hash = script.hash
|
||||
} catch (e) {
|
||||
sendUserToast('Could not load script: ' + e.body, true)
|
||||
@@ -651,15 +647,7 @@
|
||||
/>
|
||||
{/snippet}
|
||||
{#if $workspaceStore && script}
|
||||
<Star
|
||||
kind="script"
|
||||
path={script.path}
|
||||
{starred}
|
||||
workspace_id={$workspaceStore}
|
||||
onStarred={(newStarred) => {
|
||||
starred = newStarred
|
||||
}}
|
||||
/>
|
||||
<Star kind="script" path={script.path} summary={script.summary} />
|
||||
{/if}
|
||||
{#if script?.codebase}
|
||||
<Badge
|
||||
|
||||
Reference in New Issue
Block a user