Skip to content

Commit

Permalink
Feat/support tg front (#93)
Browse files Browse the repository at this point in the history
* chore: editor fixes

* feat: telegram init

* fix: slots_node generate fix

* chore: build menu tg support improvements

* fix: new modals mouseonpane check fix

* fix: undo redo mouseonpane check fix

* chore: console log delete

* fixes: mini fixes

* update version
  • Loading branch information
mxerf authored Oct 1, 2024
1 parent 1de852f commit 231da40
Show file tree
Hide file tree
Showing 31 changed files with 702 additions and 329 deletions.
2 changes: 1 addition & 1 deletion backend/chatsky_ui/api/api_v1/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from fastapi import APIRouter

from chatsky_ui.api.api_v1.endpoints import bot, config, chatsky_services, flows
from chatsky_ui.api.api_v1.endpoints import bot, chatsky_services, config, flows
from chatsky_ui.core.config import settings

api_router = APIRouter()
Expand Down
2 changes: 1 addition & 1 deletion backend/chatsky_ui/api/api_v1/endpoints/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
from chatsky_ui.api import deps
from chatsky_ui.schemas.pagination import Pagination
from chatsky_ui.schemas.preset import Preset
from chatsky_ui.schemas.process_status import Status
from chatsky_ui.services.index import Index
from chatsky_ui.services.process_manager import BuildManager, ProcessManager, RunManager
from chatsky_ui.services.websocket_manager import WebSocketManager
from chatsky_ui.schemas.process_status import Status

router = APIRouter()

Expand Down
1 change: 0 additions & 1 deletion backend/chatsky_ui/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ def init(
"https://github.com/Ramimashkouk/df_d_template.git",
no_input=no_input,
overwrite_if_exists=overwrite_if_exists,
checkout="feat/add-slots2"
)
finally:
os.chdir(original_dir)
2 changes: 1 addition & 1 deletion backend/chatsky_ui/services/condition_finder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ast
from ast import NodeTransformer
from typing import List, Dict
from typing import Dict, List

from chatsky_ui.core.logger_config import get_logger

Expand Down
18 changes: 5 additions & 13 deletions backend/chatsky_ui/services/json_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
Converts a user project's frontend graph to a script understandable by Chatsky json-importer.
"""
import ast
from collections import defaultdict
from pathlib import Path
from typing import List, Optional, Tuple
from collections import defaultdict

from omegaconf.dictconfig import DictConfig

Expand All @@ -24,9 +24,6 @@
PRE_TRANSITION = "PRE_TRANSITION"


PRE_TRANSITION = "PRE_TRANSITION"


def _get_db_paths(build_id: int) -> Tuple[Path, Path, Path, Path]:
"""Get paths to frontend graph, chatsky script, and chatsky custom conditions files."""
frontend_graph_path = settings.frontend_flows_path
Expand Down Expand Up @@ -208,7 +205,7 @@ def map_interface(interface: DictConfig) -> dict:
if not isinstance(interface, DictConfig):
raise ValueError(f"Interface must be a dictionary. Got: {type(interface)}")
keys = interface.keys()
if len(keys)!=1:
if len(keys) != 1:
raise ValueError("There must be only one key in the interface")

key = next(iter(keys))
Expand All @@ -217,18 +214,13 @@ def map_interface(interface: DictConfig) -> dict:
raise ValueError("Token keyworkd is not provided for telegram interface")
if not interface[key]["token"]:
raise ValueError("Token is not provided for telegram interface")
return {
"chatsky.messengers.telegram.LongpollingInterface": {
"token": interface[key]["token"]
}
}
return {"chatsky.messengers.telegram.LongpollingInterface": {"token": interface[key]["token"]}}
if key == "cli":
return {
"chatsky.messengers.console.CLIMessengerInterface": {}
}
return {"chatsky.messengers.console.CLIMessengerInterface": {}}
else:
raise ValueError(f"Unknown interface: {key}")


async def converter(build_id: int) -> None:
"""Translate frontend flow script into chatsky script."""
frontend_graph_path, script_path, custom_conditions_file, custom_responses_file = _get_db_paths(build_id)
Expand Down
2 changes: 1 addition & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "chatsky-ui"
version = "0.2.0"
version = "0.3.0"
description = "Chatsky-UI is GUI for Chatsky Framework, that is a free and open-source software stack for creating chatbots, released under the terms of Apache License 2.0."
license = "Apache-2.0"
authors = [
Expand Down
3 changes: 2 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import os
import sys
from chatsky_ui import __version__

sys.path.insert(0, os.path.abspath(".."))
# -- Project information -----------------------------------------------------
Expand All @@ -13,7 +14,7 @@
project = 'Chatsky-UI'
copyright = '2024, Denis Kuznetsov, Maks Rogatkin, Rami Mashkouk'
author = 'Denis Kuznetsov, Maks Rogatkin, Rami Mashkouk'
release = '0.2.0'
release = __version__

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/maste r/usage/configuration.html#general-configuration
Expand Down
Binary file modified frontend/bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@nextui-org/react": "^2.2.9",
"@radix-ui/react-context-menu": "^2.1.5",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-select": "^2.1.1",
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/UI/Code.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import classNames from 'classnames'
import React from 'react'

const Code = ({ children, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div className={classNames(
"inline text-foreground text-xs bg-table-background rounded-md px-1 py-0.5 font-mono whitespace-nowrap",
props.className
)} {...props} >{children}</div>
)
}

export default Code
129 changes: 129 additions & 0 deletions frontend/src/UI/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"
import classNames from "classnames"
import React, { forwardRef } from "react"

export type DropdownItemType = {
label: string
value: string
className?: string
icon?: React.ReactNode
disabled?: boolean
shortcut?: string
onClick?: () => void
}

export type DropdownGroupType = {
title?: string
items: DropdownItemType[]
}

interface DropdownProps {
groups: DropdownGroupType[]
onSelect: (value: string) => void
triggerContent: React.ReactNode
}

const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
({ groups, onSelect, triggerContent }, ref) => {
// const [highlightedIndex, setHighlightedIndex] = useState<number | null>(null)
// const [currentGroupIndex, setCurrentGroupIndex] = useState<number | null>(null)

// const handleKeyDown = (event: React.KeyboardEvent) => {
// const allItems = groups.flatMap((group) => group.items)

// if (event.key === "ArrowDown") {
// if (highlightedIndex === null || currentGroupIndex === null) {
// setHighlightedIndex(0)
// setCurrentGroupIndex(0)
// } else {
// setHighlightedIndex((prev) =>
// prev === null ? 0 : Math.min(prev + 1, allItems.length - 1)
// )
// }
// } else if (event.key === "ArrowUp") {
// setHighlightedIndex((prev) => (prev === null ? 0 : Math.max(prev - 1, 0)))
// } else if (event.key === "Enter" && highlightedIndex !== null && currentGroupIndex !== null) {
// const selectedItem = allItems[highlightedIndex]
// if (!selectedItem.disabled) {
// onSelect(selectedItem.value)
// selectedItem.onClick?.()
// }
// }
// }

return (
<DropdownMenu.Root>
{/* Триггер для Dropdown */}
<DropdownMenu.Trigger
autoFocus={false}
asChild>
<div
autoFocus={false}
className='focus:outline-1 outline-border rounded-lg group'
tabIndex={0}>
{triggerContent}
</div>
</DropdownMenu.Trigger>

{/* Контент Dropdown */}
<DropdownMenu.Portal>
<DropdownMenu.Content
onCloseAutoFocus={(event) => {
event.preventDefault()
}}
asChild
key={"dropdown-content"}
sideOffset={5}
align='start'
side='bottom'
className='z-[99] min-w-56 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out data-[state=open]:zoom-in'>
<div
className={classNames(
"bg-background border border-border rounded-xl p-2 origin-top-left"
)}>
{groups.map((group, groupIndex) => (
<DropdownMenu.Group
title={group.title}
key={`group-${groupIndex}`}
className='grid gap-1'>
{group.items.map((item, index) => (
<DropdownMenu.Item
key={item.value}
onSelect={() => {
if (!item.disabled) {
onSelect(item.value)
item.onClick?.()
}
}}
disabled={item.disabled}
className={classNames(
`flex items-center justify-between px-3 py-2 rounded-lg outline-none transition-colors !duration-150 data-[highlighted]:bg-bg-secondary border border-transparent data-[highlighted]:border-border ${item.disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"} `,
item.className
)}>
{/* Иконка */}
<div className='flex items-center'>
{item.icon && <span className='mr-2'>{item.icon}</span>}
<span>{item.label}</span>
</div>
{/* Шорткат */}
{item.shortcut && (
<span className='ml-auto text-gray-400'>{item.shortcut}</span>
)}
</DropdownMenu.Item>
))}

{/* Разделитель между группами */}
{groupIndex < groups.length - 1 && (
<DropdownMenu.Separator className='my-2 h-px bg-input-border' />
)}
</DropdownMenu.Group>
))}
</div>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
)
}
)

export default Dropdown
9 changes: 9 additions & 0 deletions frontend/src/UI/Loader/Loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import classNames from "classnames"
import React from "react"
import "./loader.css"

const Loader = ({ className }: React.HTMLAttributes<HTMLSpanElement>) => {
return <span className={classNames("w-5 h-5 inline-block border border-foreground !border-b-transparent rounded-full loader-rotation", className)}></span>
}

export default Loader
12 changes: 12 additions & 0 deletions frontend/src/UI/Loader/loader.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.loader-rotation {
animation: rotation 1s linear infinite;
}

@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
14 changes: 14 additions & 0 deletions frontend/src/UI/MotionContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { HTMLMotionProps, motion } from "framer-motion"
const MotionContent = ({ children, ...props }: HTMLMotionProps<"div">) => {
return (
<motion.div
{...props}
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}>
{children}
</motion.div>
)
}

export default MotionContent
14 changes: 13 additions & 1 deletion frontend/src/api/flows.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { interfaceType } from "@/contexts/flowContext"
import { $v1 } from "."
import { FlowType } from "../types/FlowTypes"
import { ParsedSlot } from "../utils"
Expand All @@ -9,10 +10,21 @@ export const get_flows = async (): Promise<GetFlowsResponseType> => {
return (await $v1.get("/flows")).data
}

export const save_flows = async (flows: FlowType[], slots?: Record<string, ParsedSlot> | null): Promise<SaveFlowsResponseType> => {
export const save_flows = async (flows: FlowType[], _interface: interfaceType, slots?: Record<string, ParsedSlot> | null): Promise<SaveFlowsResponseType> => {
// const hasValidSlots = slots && Object.values(slots).some(slot => Object.keys(slot).length > 0);
const _i = _interface.interface === "tg" ? {
telegram: {
token: _interface.token
}
} : {
cli: {}
}
const json = {
flows,
slots: slots ?? {},
interface: {
..._i
}
}
return (await $v1.post("/flows", json)).data
}
Loading

0 comments on commit 231da40

Please sign in to comment.