Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,5 @@ CLAUDE.local.md
# ignore merged schema
data/merged_schema.graphql

# webui release download temp directories
temp-webui-*
backend.ai-webui-bundle-*.zip
# webui release download temp directories (safe to delete)
scripts/temp-releases/
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { App, Form, GetProps, Input, theme, Typography } from 'antd';
import { createStyles } from 'antd-style';
import _ from 'lodash';
import { CornerDownLeftIcon } from 'lucide-react';
import { use, useState } from 'react';
import { use, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';

interface ServerError extends Error {
Expand Down Expand Up @@ -62,6 +62,7 @@ const EditableFileName: React.FC<EditableNameProps> = ({
style,
...props
}) => {
'use memo';
const { t } = useTranslation();
const { token } = theme.useToken();
const { modal, message } = App.useApp();
Expand Down Expand Up @@ -104,10 +105,20 @@ const EditableFileName: React.FC<EditableNameProps> = ({
const isPendingRenamingAndRefreshing =
renameMutation.isPending || optimisticName !== fileInfo.name;

// focus back to the text component after editing for better UX related to keyboard shortcuts
const textRef = useRef<HTMLElement>(null);
const focusFallback = () => {
setTimeout(() => {
textRef.current?.focus();
}, 0);
};

return (
<>
{!isEditing || isPendingRenamingAndRefreshing ? (
<Component
ref={textRef}
tabIndex={-1}
editable={
!disabled && !isPendingRenamingAndRefreshing
? {
Expand All @@ -123,7 +134,11 @@ const EditableFileName: React.FC<EditableNameProps> = ({
: false
}
className={!disabled ? styles.hoverEdit : undefined}
style={style}
style={{
// after editing, focus this element, remove outline
outline: 'none',
...style,
}}
{...props}
>
{fileInfo?.type === 'DIRECTORY' ? (
Expand Down Expand Up @@ -167,6 +182,7 @@ const EditableFileName: React.FC<EditableNameProps> = ({
initialValues={{ newName: fileInfo?.name }}
onFinish={(values) => {
setIsEditing(false);
focusFallback();
setOptimisticName(values.newName);
const variables = {
target_path: _.join([currentPath, fileInfo?.name], '/'),
Expand Down Expand Up @@ -242,7 +258,9 @@ const EditableFileName: React.FC<EditableNameProps> = ({
autoFocus
onKeyDown={(e) => {
if (e.key === 'Escape') {
e.stopPropagation();
setIsEditing(false);
focusFallback();
}
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useCurrentProjectValue } from '../../hooks/useCurrentProject';
import { useValidateSessionName } from '../../hooks/useValidateSessionName';
import { theme, Form, Input, App, GetProps, Typography } from 'antd';
import { CornerDownLeftIcon } from 'lucide-react';
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
graphql,
Expand Down Expand Up @@ -36,6 +36,7 @@ const EditableSessionName: React.FC<EditableSessionNameProps> = ({
style,
...otherProps
}) => {
'use memo';
const relayEvn = useRelayEnvironment();
const currentProject = useCurrentProjectValue();
const session = useFragment(
Expand Down Expand Up @@ -89,10 +90,21 @@ const EditableSessionName: React.FC<EditableSessionNameProps> = ({

const isPendingRenamingAndRefreshing =
renameSessionMutation.isPending || optimisticName !== session.name;

// focus back to the text component after editing for better UX related to keyboard shortcuts
const textRef = useRef<HTMLElement>(null);
const focusFallback = () => {
setTimeout(() => {
textRef.current?.focus();
}, 0);
};

return (
<>
{(!isEditing || isPendingRenamingAndRefreshing) && (
<Component
ref={textRef}
tabIndex={-1}
editable={
isEditingAllowed && !isPendingRenamingAndRefreshing
? {
Expand All @@ -105,6 +117,8 @@ const EditableSessionName: React.FC<EditableSessionNameProps> = ({
}
copyable
style={{
// after editing, focus this element, remove outline
outline: 'none',
...style,
color: isPendingRenamingAndRefreshing
? token.colorTextTertiary
Expand All @@ -121,6 +135,7 @@ const EditableSessionName: React.FC<EditableSessionNameProps> = ({
<Form
onFinish={(values) => {
setIsEditing(false);
focusFallback();
setOptimisticName(values.sessionName);
// FIXME: This API does not return any response on success or error.
renameSessionMutation.mutate(values.sessionName, {
Expand Down Expand Up @@ -190,7 +205,9 @@ const EditableSessionName: React.FC<EditableSessionNameProps> = ({
onKeyDown={(e) => {
// when press escape key, cancel editing
if (e.key === 'Escape') {
e.stopPropagation();
setIsEditing(false);
focusFallback();
}
}}
/>
Expand Down
18 changes: 17 additions & 1 deletion react/src/components/EditableVFolderName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { BAILink, toLocalId, useErrorMessageResolver } from 'backend.ai-ui';
import _ from 'lodash';
import { CornerDownLeftIcon } from 'lucide-react';
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
graphql,
Expand Down Expand Up @@ -55,6 +55,7 @@ const EditableVFolderName: React.FC<EditableVFolderNameProps> = ({
inputProps,
...otherProps
}) => {
'use memo';
const vfolder = useFragment(
graphql`
fragment EditableVFolderNameFragment on VirtualFolderNode {
Expand Down Expand Up @@ -93,10 +94,20 @@ const EditableVFolderName: React.FC<EditableVFolderNameProps> = ({
const isPendingRenameMutation =
renameMutation.isPending || optimisticName !== vfolder.name;

// focus back to the text component after editing for better UX related to keyboard shortcuts
const textRef = useRef<HTMLElement>(null);
const focusFallback = () => {
setTimeout(() => {
textRef.current?.focus();
}, 0);
};

return (
<>
{(!isEditing || isPendingRenameMutation) && (
<Component
ref={textRef}
tabIndex={-1}
editable={
isEditingAllowed && !isPendingRenameMutation
? {
Expand All @@ -118,6 +129,8 @@ const EditableVFolderName: React.FC<EditableVFolderNameProps> = ({
: false
}
style={{
// after editing, focus this element, remove outline
outline: 'none',
...style,
color: isPendingRenameMutation
? token.colorTextTertiary
Expand All @@ -142,6 +155,7 @@ const EditableVFolderName: React.FC<EditableVFolderNameProps> = ({
<Form
onFinish={(values) => {
setIsEditing(false);
focusFallback();
if (values.vfolderName === vfolder.name) {
return;
}
Expand Down Expand Up @@ -238,7 +252,9 @@ const EditableVFolderName: React.FC<EditableVFolderNameProps> = ({
onKeyDown={(e) => {
// when press escape key, cancel editing
if (e.key === 'Escape') {
e.stopPropagation();
setIsEditing(false);
focusFallback();
onEditEnd?.();
}
}}
Expand Down
1 change: 1 addition & 0 deletions react/src/components/FolderExplorerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ const FolderExplorerModal: React.FC<FolderExplorerProps> = ({
className={styles.baiModalHeader}
width={'90%'}
centered
keyboard
destroyOnHidden
footer={null}
title={
Expand Down
30 changes: 22 additions & 8 deletions scripts/serve-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,21 @@ VERSION="$1"
# Remove 'v' prefix if present
VERSION="${VERSION#v}"

# Get script directory and project root
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"

# Create temp directory structure
TEMP_BASE="${SCRIPT_DIR}/temp-releases"
mkdir -p "${TEMP_BASE}"

# Construct the download URL and filename
FILENAME="backend.ai-webui-bundle-${VERSION}.zip"
URL="https://github.com/lablup/backend.ai-webui/releases/download/v${VERSION}/${FILENAME}"

# Create a temporary directory for extraction
TEMP_DIR="temp-webui-${VERSION}"
# Set paths in temp directory
DOWNLOAD_PATH="${TEMP_BASE}/${FILENAME}"
TEMP_DIR="${TEMP_BASE}/webui-${VERSION}"

# Check if the version folder already exists
if [ -d "${TEMP_DIR}" ]; then
Expand All @@ -32,28 +41,33 @@ if [ -d "${TEMP_DIR}" ]; then
EXTRACTED_FOLDER="${TEMP_DIR}"
fi
else
echo "Downloading ${FILENAME}..."
curl -L -o "${FILENAME}" "${URL}"
echo "Downloading ${FILENAME} to ${TEMP_BASE}..."
curl -L -o "${DOWNLOAD_PATH}" "${URL}"

if [ ! -f "${FILENAME}" ]; then
if [ ! -f "${DOWNLOAD_PATH}" ]; then
echo "Error: Failed to download ${FILENAME}"
exit 1
fi

echo "Extracting ${FILENAME}..."
rm -rf "${TEMP_DIR}"
unzip -q "${FILENAME}" -d "${TEMP_DIR}"
unzip -q "${DOWNLOAD_PATH}" -d "${TEMP_DIR}"

# Find the extracted folder (it might be nested)
EXTRACTED_FOLDER=$(find "${TEMP_DIR}" -type d -name "*webui*" | head -1)
if [ -z "${EXTRACTED_FOLDER}" ]; then
# If no webui folder found, use the temp directory itself
EXTRACTED_FOLDER="${TEMP_DIR}"
fi

# Optionally remove the zip file after extraction to save space
echo "Removing downloaded zip file..."
rm "${DOWNLOAD_PATH}"
fi

echo "Copying config.toml to extracted folder..."
cp config.toml "${EXTRACTED_FOLDER}/"
echo "Overwriting config.toml and plugins to extracted folder..."
cp "${PROJECT_ROOT}/config.toml" "${EXTRACTED_FOLDER}/"
cp -r "${PROJECT_ROOT}/dist/plugins" "${EXTRACTED_FOLDER}/dist/"

echo "Starting server in ${EXTRACTED_FOLDER}..."
cd "${EXTRACTED_FOLDER}"
Expand Down