Fixed freeze implementation (#1983)
* chore: Update utils imports and add cn to nodeToolbarComponent * fix(utils.py): change key parameter name from 'flow_id' to 'key' for consistency fix(chat.py): remove unused import 'functools.partial' to improve code readability refactor(chat.py): remove 'set_cache_coro' partial function and pass 'chat_service' directly to 'build_vertex' method for better code organization feat(schemas.py): add 'used_frozen_result' field to ResultDataResponse and ResultData classes with default value of False for better tracking of frozen result usage feat(base.py): add 'chat_service' parameter to 'build_vertex' method in Graph class to allow passing ChatService instance for cache operations feat(base.py): update 'build_vertex' method in Graph class to handle caching of frozen vertices and set 'used_frozen_result' flag in ResultData class feat(cache/service.py): change parameter name from 'flow_id' to 'key' in 'set_cache' and 'get_cache' methods for consistency feat(cache/utils.py): add 'CacheMiss' class to represent cache miss situations for better error handling * feat: Add check for None before setting 'used_frozen_result' flag in Graph class * feat: Add frozen effect to buttons and improve code organization The code changes introduce a frozen effect to buttons by adding new CSS classes and styles. This effect is achieved by applying borders, shadows, and background colors. Additionally, the code is refactored to improve code organization and remove unused imports. Note: This commit message follows the convention used in the recent user commits. * feat: Add frozen effect to buttons and improve code organization * style(applies.css): Update border styles for frozen state to improve visual appearance and consistency style(applies.css): Adjust opacity of frosted background for better readability style(tailwind.config.js): Increase opacity of frozen-ring shadow for better visual effect style(tailwind.config.js): Increase opacity of frosted-ring shadow for better visual effect * feat(parameterComponent): add snowflake icon to ParameterComponent when node is frozen and not aligned left * style(applies.css): Update border styles for frozen state and add border to improve visual appearance and consistency
This commit is contained in:
parent
ee62bd38b7
commit
f694f0716f
13 changed files with 161 additions and 68 deletions
|
|
@ -286,7 +286,7 @@ async def get_next_runnable_vertices(
|
||||||
for v_id in set(next_runnable_vertices): # Use set to avoid duplicates
|
for v_id in set(next_runnable_vertices): # Use set to avoid duplicates
|
||||||
graph.vertices_to_run.remove(v_id)
|
graph.vertices_to_run.remove(v_id)
|
||||||
graph.remove_from_predecessors(v_id)
|
graph.remove_from_predecessors(v_id)
|
||||||
await chat_service.set_cache(flow_id=flow_id, data=graph, lock=lock)
|
await chat_service.set_cache(key=flow_id, data=graph, lock=lock)
|
||||||
return next_runnable_vertices
|
return next_runnable_vertices
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from functools import partial
|
|
||||||
from typing import TYPE_CHECKING, Annotated, Optional
|
from typing import TYPE_CHECKING, Annotated, Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, BackgroundTasks, Body, Depends, HTTPException
|
from fastapi import APIRouter, BackgroundTasks, Body, Depends, HTTPException
|
||||||
|
|
@ -162,7 +161,6 @@ async def build_vertex(
|
||||||
vertex = graph.get_vertex(vertex_id)
|
vertex = graph.get_vertex(vertex_id)
|
||||||
try:
|
try:
|
||||||
lock = chat_service._cache_locks[flow_id_str]
|
lock = chat_service._cache_locks[flow_id_str]
|
||||||
set_cache_coro = partial(chat_service.set_cache, flow_id=flow_id_str)
|
|
||||||
(
|
(
|
||||||
next_runnable_vertices,
|
next_runnable_vertices,
|
||||||
top_level_vertices,
|
top_level_vertices,
|
||||||
|
|
@ -173,7 +171,7 @@ async def build_vertex(
|
||||||
vertex,
|
vertex,
|
||||||
) = await graph.build_vertex(
|
) = await graph.build_vertex(
|
||||||
lock=lock,
|
lock=lock,
|
||||||
set_cache_coro=set_cache_coro,
|
chat_service=chat_service,
|
||||||
vertex_id=vertex_id,
|
vertex_id=vertex_id,
|
||||||
user_id=current_user.id,
|
user_id=current_user.id,
|
||||||
inputs_dict=inputs.model_dump() if inputs else {},
|
inputs_dict=inputs.model_dump() if inputs else {},
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,7 @@ class ResultDataResponse(BaseModel):
|
||||||
artifacts: Optional[Any] = Field(default_factory=dict)
|
artifacts: Optional[Any] = Field(default_factory=dict)
|
||||||
timedelta: Optional[float] = None
|
timedelta: Optional[float] = None
|
||||||
duration: Optional[str] = None
|
duration: Optional[str] = None
|
||||||
|
used_frozen_result: Optional[bool] = False
|
||||||
|
|
||||||
|
|
||||||
class VertexBuildResponse(BaseModel):
|
class VertexBuildResponse(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ from langflow.graph.vertex.base import Vertex
|
||||||
from langflow.graph.vertex.types import InterfaceVertex, StateVertex
|
from langflow.graph.vertex.types import InterfaceVertex, StateVertex
|
||||||
from langflow.schema import Record
|
from langflow.schema import Record
|
||||||
from langflow.schema.schema import INPUT_FIELD_NAME, InputType
|
from langflow.schema.schema import INPUT_FIELD_NAME, InputType
|
||||||
|
from langflow.services.cache.utils import CacheMiss
|
||||||
|
from langflow.services.chat.service import ChatService
|
||||||
from langflow.services.deps import get_chat_service
|
from langflow.services.deps import get_chat_service
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
@ -704,7 +706,7 @@ class Graph:
|
||||||
async def build_vertex(
|
async def build_vertex(
|
||||||
self,
|
self,
|
||||||
lock: asyncio.Lock,
|
lock: asyncio.Lock,
|
||||||
set_cache_coro: Callable[["Graph", asyncio.Lock], Coroutine],
|
chat_service: ChatService,
|
||||||
vertex_id: str,
|
vertex_id: str,
|
||||||
inputs_dict: Optional[Dict[str, str]] = None,
|
inputs_dict: Optional[Dict[str, str]] = None,
|
||||||
user_id: Optional[str] = None,
|
user_id: Optional[str] = None,
|
||||||
|
|
@ -729,17 +731,35 @@ class Graph:
|
||||||
"""
|
"""
|
||||||
vertex = self.get_vertex(vertex_id)
|
vertex = self.get_vertex(vertex_id)
|
||||||
try:
|
try:
|
||||||
if not vertex.frozen or not vertex._built:
|
params = ""
|
||||||
|
if vertex.frozen:
|
||||||
|
# Check the cache for the vertex
|
||||||
|
cached_result = await chat_service.get_cache(key=vertex.id)
|
||||||
|
if isinstance(cached_result, CacheMiss):
|
||||||
|
await vertex.build(user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars)
|
||||||
|
await chat_service.set_cache(key=vertex.id, data=vertex)
|
||||||
|
else:
|
||||||
|
cached_vertex = cached_result["result"]
|
||||||
|
# Now set update the vertex with the cached vertex
|
||||||
|
vertex._built = cached_vertex._built
|
||||||
|
vertex.result = cached_vertex.result
|
||||||
|
vertex.artifacts = cached_vertex.artifacts
|
||||||
|
vertex._built_object = cached_vertex._built_object
|
||||||
|
vertex._custom_component = cached_vertex._custom_component
|
||||||
|
if vertex.result is not None:
|
||||||
|
vertex.result.used_frozen_result = True
|
||||||
|
|
||||||
|
else:
|
||||||
await vertex.build(user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars)
|
await vertex.build(user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars)
|
||||||
|
|
||||||
if vertex.result is not None:
|
if vertex.result is not None:
|
||||||
params = vertex._built_object_repr()
|
params = f"{vertex._built_object_repr()}{params}"
|
||||||
valid = True
|
valid = True
|
||||||
result_dict = vertex.result
|
result_dict = vertex.result
|
||||||
artifacts = vertex.artifacts
|
artifacts = vertex.artifacts
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"No result found for vertex {vertex_id}")
|
raise ValueError(f"No result found for vertex {vertex_id}")
|
||||||
|
set_cache_coro = partial(chat_service.set_cache, key=self.flow_id)
|
||||||
next_runnable_vertices, top_level_vertices = await self.get_next_and_top_level_vertices(
|
next_runnable_vertices, top_level_vertices = await self.get_next_and_top_level_vertices(
|
||||||
lock, set_cache_coro, vertex
|
lock, set_cache_coro, vertex
|
||||||
)
|
)
|
||||||
|
|
@ -810,11 +830,10 @@ class Graph:
|
||||||
for vertex_id in current_batch:
|
for vertex_id in current_batch:
|
||||||
vertex = self.get_vertex(vertex_id)
|
vertex = self.get_vertex(vertex_id)
|
||||||
lock = chat_service._cache_locks[self.run_id]
|
lock = chat_service._cache_locks[self.run_id]
|
||||||
set_cache_coro = partial(chat_service.set_cache, flow_id=self.run_id)
|
|
||||||
task = asyncio.create_task(
|
task = asyncio.create_task(
|
||||||
self.build_vertex(
|
self.build_vertex(
|
||||||
lock=lock,
|
lock=lock,
|
||||||
set_cache_coro=set_cache_coro,
|
chat_service=chat_service,
|
||||||
vertex_id=vertex_id,
|
vertex_id=vertex_id,
|
||||||
user_id=self.user_id,
|
user_id=self.user_id,
|
||||||
inputs_dict={},
|
inputs_dict={},
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ class ResultData(BaseModel):
|
||||||
duration: Optional[str] = None
|
duration: Optional[str] = None
|
||||||
component_display_name: Optional[str] = None
|
component_display_name: Optional[str] = None
|
||||||
component_id: Optional[str] = None
|
component_id: Optional[str] = None
|
||||||
|
used_frozen_result: Optional[bool] = False
|
||||||
|
|
||||||
@field_serializer("results")
|
@field_serializer("results")
|
||||||
def serialize_results(self, value):
|
def serialize_results(self, value):
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ from loguru import logger
|
||||||
|
|
||||||
from langflow.services.base import Service
|
from langflow.services.base import Service
|
||||||
from langflow.services.cache.base import AsyncBaseCacheService, CacheService
|
from langflow.services.cache.base import AsyncBaseCacheService, CacheService
|
||||||
|
from langflow.services.cache.utils import CacheMiss
|
||||||
|
|
||||||
|
CACHE_MISS = CacheMiss()
|
||||||
|
|
||||||
|
|
||||||
class ThreadingInMemoryCache(CacheService, Service):
|
class ThreadingInMemoryCache(CacheService, Service):
|
||||||
|
|
@ -341,12 +344,14 @@ class AsyncInMemoryCache(AsyncBaseCacheService, Service):
|
||||||
|
|
||||||
async def _get(self, key):
|
async def _get(self, key):
|
||||||
item = self.cache.get(key, None)
|
item = self.cache.get(key, None)
|
||||||
if item and (time.time() - item["time"] < self.expiration_time):
|
|
||||||
self.cache.move_to_end(key)
|
|
||||||
return pickle.loads(item["value"]) if isinstance(item["value"], bytes) else item["value"]
|
|
||||||
if item:
|
if item:
|
||||||
await self.delete(key)
|
if time.time() - item["time"] < self.expiration_time:
|
||||||
return None
|
self.cache.move_to_end(key)
|
||||||
|
return pickle.loads(item["value"]) if isinstance(item["value"], bytes) else item["value"]
|
||||||
|
else:
|
||||||
|
logger.info(f"Cache item for key '{key}' has expired and will be deleted.")
|
||||||
|
await self.delete(key) # Log before deleting the expired item
|
||||||
|
return CACHE_MISS
|
||||||
|
|
||||||
async def set(self, key, value, lock: Optional[asyncio.Lock] = None):
|
async def set(self, key, value, lock: Optional[asyncio.Lock] = None):
|
||||||
if not lock:
|
if not lock:
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,11 @@ CACHE_DIR = user_cache_dir("langflow", "langflow")
|
||||||
PREFIX = "langflow_cache"
|
PREFIX = "langflow_cache"
|
||||||
|
|
||||||
|
|
||||||
|
class CacheMiss:
|
||||||
|
def __repr__(self):
|
||||||
|
return "<CACHE_MISS>"
|
||||||
|
|
||||||
|
|
||||||
def create_cache_folder(func):
|
def create_cache_folder(func):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
# Get the destination folder
|
# Get the destination folder
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class ChatService(Service):
|
||||||
self._cache_locks = defaultdict(asyncio.Lock)
|
self._cache_locks = defaultdict(asyncio.Lock)
|
||||||
self.cache_service = get_cache_service()
|
self.cache_service = get_cache_service()
|
||||||
|
|
||||||
async def set_cache(self, flow_id: str, data: Any, lock: Optional[asyncio.Lock] = None) -> bool:
|
async def set_cache(self, key: str, data: Any, lock: Optional[asyncio.Lock] = None) -> bool:
|
||||||
"""
|
"""
|
||||||
Set the cache for a client.
|
Set the cache for a client.
|
||||||
"""
|
"""
|
||||||
|
|
@ -23,17 +23,17 @@ class ChatService(Service):
|
||||||
"result": data,
|
"result": data,
|
||||||
"type": type(data),
|
"type": type(data),
|
||||||
}
|
}
|
||||||
await self.cache_service.upsert(flow_id, result_dict, lock=lock or self._cache_locks[flow_id])
|
await self.cache_service.upsert(key, result_dict, lock=lock or self._cache_locks[key])
|
||||||
return flow_id in self.cache_service
|
return key in self.cache_service
|
||||||
|
|
||||||
async def get_cache(self, flow_id: str, lock: Optional[asyncio.Lock] = None) -> Any:
|
async def get_cache(self, key: str, lock: Optional[asyncio.Lock] = None) -> Any:
|
||||||
"""
|
"""
|
||||||
Get the cache for a client.
|
Get the cache for a client.
|
||||||
"""
|
"""
|
||||||
return await self.cache_service.get(flow_id, lock=lock or self._cache_locks[flow_id])
|
return await self.cache_service.get(key, lock=lock or self._cache_locks[key])
|
||||||
|
|
||||||
async def clear_cache(self, flow_id: str, lock: Optional[asyncio.Lock] = None):
|
async def clear_cache(self, key: str, lock: Optional[asyncio.Lock] = None):
|
||||||
"""
|
"""
|
||||||
Clear the cache for a client.
|
Clear the cache for a client.
|
||||||
"""
|
"""
|
||||||
await self.cache_service.delete(flow_id, lock=lock or self._cache_locks[flow_id])
|
await self.cache_service.delete(key, lock=lock or self._cache_locks[key])
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ export default function ParameterComponent({
|
||||||
setNode,
|
setNode,
|
||||||
renderTooltips,
|
renderTooltips,
|
||||||
isLoading,
|
isLoading,
|
||||||
setIsLoading,
|
setIsLoading
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass(
|
const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass(
|
||||||
|
|
@ -98,7 +98,7 @@ export default function ParameterComponent({
|
||||||
takeSnapshot,
|
takeSnapshot,
|
||||||
setNode,
|
setNode,
|
||||||
updateNodeInternals,
|
updateNodeInternals,
|
||||||
renderTooltips,
|
renderTooltips
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleRefreshButtonPress: handleRefreshButtonPressHook } =
|
const { handleRefreshButtonPress: handleRefreshButtonPressHook } =
|
||||||
|
|
@ -107,7 +107,7 @@ export default function ParameterComponent({
|
||||||
let disabled =
|
let disabled =
|
||||||
edges.some(
|
edges.some(
|
||||||
(edge) =>
|
(edge) =>
|
||||||
edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id),
|
edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id)
|
||||||
) ?? false;
|
) ?? false;
|
||||||
|
|
||||||
const handleRefreshButtonPress = async (name, data) => {
|
const handleRefreshButtonPress = async (name, data) => {
|
||||||
|
|
@ -120,12 +120,12 @@ export default function ParameterComponent({
|
||||||
handleUpdateValues,
|
handleUpdateValues,
|
||||||
setNode,
|
setNode,
|
||||||
renderTooltips,
|
renderTooltips,
|
||||||
setIsLoading,
|
setIsLoading
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnNewValue = async (
|
const handleOnNewValue = async (
|
||||||
newValue: string | string[] | boolean | Object[],
|
newValue: string | string[] | boolean | Object[],
|
||||||
skipSnapshot: boolean | undefined = false,
|
skipSnapshot: boolean | undefined = false
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
handleOnNewValueHook(newValue, skipSnapshot);
|
handleOnNewValueHook(newValue, skipSnapshot);
|
||||||
};
|
};
|
||||||
|
|
@ -207,7 +207,7 @@ export default function ParameterComponent({
|
||||||
className={classNames(
|
className={classNames(
|
||||||
left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ",
|
left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ",
|
||||||
"h-3 w-3 rounded-full border-2 bg-background",
|
"h-3 w-3 rounded-full border-2 bg-background",
|
||||||
!showNode ? "mt-0" : "",
|
!showNode ? "mt-0" : ""
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
borderColor: color ?? nodeColors.unknown,
|
borderColor: color ?? nodeColors.unknown,
|
||||||
|
|
@ -238,7 +238,7 @@ export default function ParameterComponent({
|
||||||
(left ? "" : " justify-end")
|
(left ? "" : " justify-end")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Case condition={left && data.node?.frozen}>
|
<Case condition={!left && data.node?.frozen}>
|
||||||
<div className="pr-1">
|
<div className="pr-1">
|
||||||
<IconComponent className="h-5 w-5 text-ice" name={"Snowflake"} />
|
<IconComponent className="h-5 w-5 text-ice" name={"Snowflake"} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -296,7 +296,7 @@ export default function ParameterComponent({
|
||||||
}
|
}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
left ? "-ml-0.5" : "-mr-0.5",
|
left ? "-ml-0.5" : "-mr-0.5",
|
||||||
"h-3 w-3 rounded-full border-2 bg-background",
|
"h-3 w-3 rounded-full border-2 bg-background"
|
||||||
)}
|
)}
|
||||||
style={{ borderColor: color ?? nodeColors.unknown }}
|
style={{ borderColor: color ?? nodeColors.unknown }}
|
||||||
onClick={() => setFilterEdge(groupedEdge.current)}
|
onClick={() => setFilterEdge(groupedEdge.current)}
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@ import { NodeDataType } from "../../types/flow";
|
||||||
import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
|
import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
|
||||||
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
||||||
import { classNames, cn } from "../../utils/utils";
|
import { classNames, cn } from "../../utils/utils";
|
||||||
import ParameterComponent from "./components/parameterComponent";
|
|
||||||
import getFieldTitle from "../utils/get-field-title";
|
import getFieldTitle from "../utils/get-field-title";
|
||||||
import sortFields from "../utils/sort-fields";
|
import sortFields from "../utils/sort-fields";
|
||||||
|
import ParameterComponent from "./components/parameterComponent";
|
||||||
|
|
||||||
export default function GenericNode({
|
export default function GenericNode({
|
||||||
data,
|
data,
|
||||||
|
|
@ -56,14 +56,14 @@ export default function GenericNode({
|
||||||
const [nodeName, setNodeName] = useState(data.node!.display_name);
|
const [nodeName, setNodeName] = useState(data.node!.display_name);
|
||||||
const [inputDescription, setInputDescription] = useState(false);
|
const [inputDescription, setInputDescription] = useState(false);
|
||||||
const [nodeDescription, setNodeDescription] = useState(
|
const [nodeDescription, setNodeDescription] = useState(
|
||||||
data.node?.description!,
|
data.node?.description!
|
||||||
);
|
);
|
||||||
const [isOutdated, setIsOutdated] = useState(false);
|
const [isOutdated, setIsOutdated] = useState(false);
|
||||||
const buildStatus = useFlowStore(
|
const buildStatus = useFlowStore(
|
||||||
(state) => state.flowBuildStatus[data.id]?.status,
|
(state) => state.flowBuildStatus[data.id]?.status
|
||||||
);
|
);
|
||||||
const lastRunTime = useFlowStore(
|
const lastRunTime = useFlowStore(
|
||||||
(state) => state.flowBuildStatus[data.id]?.timestamp,
|
(state) => state.flowBuildStatus[data.id]?.timestamp
|
||||||
);
|
);
|
||||||
const [validationStatus, setValidationStatus] =
|
const [validationStatus, setValidationStatus] =
|
||||||
useState<validationStatusType | null>(null);
|
useState<validationStatusType | null>(null);
|
||||||
|
|
@ -120,7 +120,7 @@ export default function GenericNode({
|
||||||
|
|
||||||
updateNodeInternals(data.id);
|
updateNodeInternals(data.id);
|
||||||
},
|
},
|
||||||
[data.id, data.node, setNode, setIsOutdated],
|
[data.id, data.node, setNode, setIsOutdated]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!data.node!.template) {
|
if (!data.node!.template) {
|
||||||
|
|
@ -260,7 +260,7 @@ export default function GenericNode({
|
||||||
const isDark = useDarkStore((state) => state.dark);
|
const isDark = useDarkStore((state) => state.dark);
|
||||||
const renderIconStatus = (
|
const renderIconStatus = (
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null,
|
validationStatus: validationStatusType | null
|
||||||
) => {
|
) => {
|
||||||
if (buildStatus === BuildStatus.BUILDING) {
|
if (buildStatus === BuildStatus.BUILDING) {
|
||||||
return <Loading className="text-medium-indigo" />;
|
return <Loading className="text-medium-indigo" />;
|
||||||
|
|
@ -301,7 +301,7 @@ export default function GenericNode({
|
||||||
};
|
};
|
||||||
const getSpecificClassFromBuildStatus = (
|
const getSpecificClassFromBuildStatus = (
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null,
|
validationStatus: validationStatusType | null
|
||||||
) => {
|
) => {
|
||||||
let isInvalid = validationStatus && !validationStatus.valid;
|
let isInvalid = validationStatus && !validationStatus.valid;
|
||||||
|
|
||||||
|
|
@ -325,25 +325,31 @@ export default function GenericNode({
|
||||||
selected: boolean,
|
selected: boolean,
|
||||||
showNode: boolean,
|
showNode: boolean,
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null,
|
validationStatus: validationStatusType | null
|
||||||
) => {
|
) => {
|
||||||
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
|
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
|
||||||
buildStatus,
|
buildStatus,
|
||||||
validationStatus,
|
validationStatus
|
||||||
);
|
);
|
||||||
|
|
||||||
const baseBorderClass = getBaseBorderClass(selected);
|
const baseBorderClass = getBaseBorderClass(selected);
|
||||||
const nodeSizeClass = getNodeSizeClass(showNode);
|
const nodeSizeClass = getNodeSizeClass(showNode);
|
||||||
return classNames(
|
const names = classNames(
|
||||||
baseBorderClass,
|
baseBorderClass,
|
||||||
nodeSizeClass,
|
nodeSizeClass,
|
||||||
"generic-node-div",
|
"generic-node-div",
|
||||||
specificClassFromBuildStatus,
|
specificClassFromBuildStatus
|
||||||
);
|
);
|
||||||
|
console.log("names", names);
|
||||||
|
return names;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBaseBorderClass = (selected) =>
|
const getBaseBorderClass = (selected) => {
|
||||||
selected ? "border border-ring" : "border";
|
console.log("data.node?.frozen", data.node?.frozen);
|
||||||
|
let className = selected ? "border border-ring" : "border";
|
||||||
|
let frozenClass = selected ? "border-ring-frozen" : "border-frozen";
|
||||||
|
return data.node?.frozen ? frozenClass : className;
|
||||||
|
};
|
||||||
|
|
||||||
const getNodeSizeClass = (showNode) =>
|
const getNodeSizeClass = (showNode) =>
|
||||||
showNode ? "w-96 rounded-lg" : "w-26 h-26 rounded-full";
|
showNode ? "w-96 rounded-lg" : "w-26 h-26 rounded-full";
|
||||||
|
|
@ -394,7 +400,7 @@ export default function GenericNode({
|
||||||
selected,
|
selected,
|
||||||
showNode,
|
showNode,
|
||||||
buildStatus,
|
buildStatus,
|
||||||
validationStatus,
|
validationStatus
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{data.node?.beta && showNode && (
|
{data.node?.beta && showNode && (
|
||||||
|
|
@ -539,7 +545,7 @@ export default function GenericNode({
|
||||||
}
|
}
|
||||||
title={getFieldTitle(
|
title={getFieldTitle(
|
||||||
data.node?.template!,
|
data.node?.template!,
|
||||||
templateField,
|
templateField
|
||||||
)}
|
)}
|
||||||
info={data.node?.template[templateField].info}
|
info={data.node?.template[templateField].info}
|
||||||
name={templateField}
|
name={templateField}
|
||||||
|
|
@ -567,7 +573,7 @@ export default function GenericNode({
|
||||||
proxy={data.node?.template[templateField].proxy}
|
proxy={data.node?.template[templateField].proxy}
|
||||||
showNode={showNode}
|
showNode={showNode}
|
||||||
/>
|
/>
|
||||||
),
|
)
|
||||||
)}
|
)}
|
||||||
<ParameterComponent
|
<ParameterComponent
|
||||||
key={scapedJSONStringfy({
|
key={scapedJSONStringfy({
|
||||||
|
|
@ -724,7 +730,7 @@ export default function GenericNode({
|
||||||
!data.node?.description) &&
|
!data.node?.description) &&
|
||||||
nameEditable
|
nameEditable
|
||||||
? "font-light italic"
|
? "font-light italic"
|
||||||
: "",
|
: ""
|
||||||
)}
|
)}
|
||||||
onDoubleClick={(e) => {
|
onDoubleClick={(e) => {
|
||||||
setInputDescription(true);
|
setInputDescription(true);
|
||||||
|
|
@ -786,13 +792,13 @@ export default function GenericNode({
|
||||||
}
|
}
|
||||||
title={getFieldTitle(
|
title={getFieldTitle(
|
||||||
data.node?.template!,
|
data.node?.template!,
|
||||||
templateField,
|
templateField
|
||||||
)}
|
)}
|
||||||
info={data.node?.template[templateField].info}
|
info={data.node?.template[templateField].info}
|
||||||
name={templateField}
|
name={templateField}
|
||||||
tooltipTitle={
|
tooltipTitle={
|
||||||
data.node?.template[templateField].input_types?.join(
|
data.node?.template[templateField].input_types?.join(
|
||||||
"\n",
|
"\n"
|
||||||
) ?? data.node?.template[templateField].type
|
) ?? data.node?.template[templateField].type
|
||||||
}
|
}
|
||||||
required={data.node!.template[templateField].required}
|
required={data.node!.template[templateField].required}
|
||||||
|
|
@ -819,7 +825,7 @@ export default function GenericNode({
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
||||||
"flex-max-width justify-center",
|
"flex-max-width justify-center"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{" "}
|
{" "}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import {
|
||||||
expandGroupNode,
|
expandGroupNode,
|
||||||
updateFlowPosition,
|
updateFlowPosition,
|
||||||
} from "../../../../utils/reactflowUtils";
|
} from "../../../../utils/reactflowUtils";
|
||||||
import { classNames } from "../../../../utils/utils";
|
import { classNames, cn } from "../../../../utils/utils";
|
||||||
import ToolbarSelectItem from "./toolbarSelectItem";
|
import ToolbarSelectItem from "./toolbarSelectItem";
|
||||||
|
|
||||||
export default function NodeToolbarComponent({
|
export default function NodeToolbarComponent({
|
||||||
|
|
@ -58,7 +58,7 @@ export default function NodeToolbarComponent({
|
||||||
data.node.template[templateField].type === "Any" ||
|
data.node.template[templateField].type === "Any" ||
|
||||||
data.node.template[templateField].type === "int" ||
|
data.node.template[templateField].type === "int" ||
|
||||||
data.node.template[templateField].type === "dict" ||
|
data.node.template[templateField].type === "dict" ||
|
||||||
data.node.template[templateField].type === "NestedDict"),
|
data.node.template[templateField].type === "NestedDict")
|
||||||
).length;
|
).length;
|
||||||
const templates = useTypesStore((state) => state.templates);
|
const templates = useTypesStore((state) => state.templates);
|
||||||
const hasStore = useStoreStore((state) => state.hasStore);
|
const hasStore = useStoreStore((state) => state.hasStore);
|
||||||
|
|
@ -68,7 +68,7 @@ export default function NodeToolbarComponent({
|
||||||
const isMinimal = numberOfHandles <= 1;
|
const isMinimal = numberOfHandles <= 1;
|
||||||
const isGroup = data.node?.flow ? true : false;
|
const isGroup = data.node?.flow ? true : false;
|
||||||
|
|
||||||
// const frozen = data.node?.frozen ?? false;
|
const frozen = data.node?.frozen ?? false;
|
||||||
const paste = useFlowStore((state) => state.paste);
|
const paste = useFlowStore((state) => state.paste);
|
||||||
const nodes = useFlowStore((state) => state.nodes);
|
const nodes = useFlowStore((state) => state.nodes);
|
||||||
const edges = useFlowStore((state) => state.edges);
|
const edges = useFlowStore((state) => state.edges);
|
||||||
|
|
@ -85,7 +85,7 @@ export default function NodeToolbarComponent({
|
||||||
const [showconfirmShare, setShowconfirmShare] = useState(false);
|
const [showconfirmShare, setShowconfirmShare] = useState(false);
|
||||||
const [showOverrideModal, setShowOverrideModal] = useState(false);
|
const [showOverrideModal, setShowOverrideModal] = useState(false);
|
||||||
const [flowComponent, setFlowComponent] = useState<FlowType>(
|
const [flowComponent, setFlowComponent] = useState<FlowType>(
|
||||||
createFlowComponent(cloneDeep(data), version),
|
createFlowComponent(cloneDeep(data), version)
|
||||||
);
|
);
|
||||||
|
|
||||||
const openInNewTab = (url) => {
|
const openInNewTab = (url) => {
|
||||||
|
|
@ -100,7 +100,7 @@ export default function NodeToolbarComponent({
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
const updateNodeInternals = useUpdateNodeInternals();
|
||||||
|
|
||||||
const setLastCopiedSelection = useFlowStore(
|
const setLastCopiedSelection = useFlowStore(
|
||||||
(state) => state.setLastCopiedSelection,
|
(state) => state.setLastCopiedSelection
|
||||||
);
|
);
|
||||||
|
|
||||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||||
|
|
@ -150,7 +150,7 @@ export default function NodeToolbarComponent({
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
setNodes,
|
setNodes,
|
||||||
setEdges,
|
setEdges
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "override":
|
case "override":
|
||||||
|
|
@ -174,7 +174,7 @@ export default function NodeToolbarComponent({
|
||||||
y: 10,
|
y: 10,
|
||||||
paneX: nodes.find((node) => node.id === data.id)?.position.x,
|
paneX: nodes.find((node) => node.id === data.id)?.position.x,
|
||||||
paneY: nodes.find((node) => node.id === data.id)?.position.y,
|
paneY: nodes.find((node) => node.id === data.id)?.position.y,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "update":
|
case "update":
|
||||||
|
|
@ -212,13 +212,13 @@ export default function NodeToolbarComponent({
|
||||||
};
|
};
|
||||||
|
|
||||||
const isSaved = flows.some((flow) =>
|
const isSaved = flows.some((flow) =>
|
||||||
Object.values(flow).includes(data.node?.display_name!),
|
Object.values(flow).includes(data.node?.display_name!)
|
||||||
);
|
);
|
||||||
|
|
||||||
const setNode = useFlowStore((state) => state.setNode);
|
const setNode = useFlowStore((state) => state.setNode);
|
||||||
|
|
||||||
const handleOnNewValue = (
|
const handleOnNewValue = (
|
||||||
newValue: string | string[] | boolean | Object[],
|
newValue: string | string[] | boolean | Object[]
|
||||||
): void => {
|
): void => {
|
||||||
if (data.node!.template[name].value !== newValue) {
|
if (data.node!.template[name].value !== newValue) {
|
||||||
takeSnapshot();
|
takeSnapshot();
|
||||||
|
|
@ -401,7 +401,7 @@ export default function NodeToolbarComponent({
|
||||||
data-testid="save-button-modal"
|
data-testid="save-button-modal"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
|
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
|
||||||
hasCode ? " " : " rounded-l-md ",
|
hasCode ? " " : " rounded-l-md "
|
||||||
)}
|
)}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
@ -419,7 +419,7 @@ export default function NodeToolbarComponent({
|
||||||
<button
|
<button
|
||||||
data-testid="duplicate-button-modal"
|
data-testid="duplicate-button-modal"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
|
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
|
||||||
)}
|
)}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
@ -430,7 +430,7 @@ export default function NodeToolbarComponent({
|
||||||
</button>
|
</button>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
|
|
||||||
{/* <ShadTooltip content="Freeze" side="top">
|
<ShadTooltip content="Freeze" side="top">
|
||||||
<button
|
<button
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
|
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
|
||||||
|
|
@ -443,7 +443,7 @@ export default function NodeToolbarComponent({
|
||||||
...old.data,
|
...old.data,
|
||||||
node: {
|
node: {
|
||||||
...old.data.node,
|
...old.data.node,
|
||||||
// frozen: old.data?.node?.frozen ? false : true,
|
frozen: old.data?.node?.frozen ? false : true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
@ -458,7 +458,7 @@ export default function NodeToolbarComponent({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</ShadTooltip> */}
|
</ShadTooltip>
|
||||||
|
|
||||||
<Select onValueChange={handleSelectChange} value="">
|
<Select onValueChange={handleSelectChange} value="">
|
||||||
<ShadTooltip content="More" side="top">
|
<ShadTooltip content="More" side="top">
|
||||||
|
|
@ -467,7 +467,7 @@ export default function NodeToolbarComponent({
|
||||||
<div
|
<div
|
||||||
data-testid="more-options-modal"
|
data-testid="more-options-modal"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
|
"relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,7 @@
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
font-feature-settings:
|
font-feature-settings: "rlig" 1, "calt" 1;
|
||||||
"rlig" 1,
|
|
||||||
"calt" 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,6 +113,57 @@
|
||||||
.button-disable {
|
.button-disable {
|
||||||
@apply pointer-events-none;
|
@apply pointer-events-none;
|
||||||
}
|
}
|
||||||
|
/* Frozen state border */
|
||||||
|
.border-ring-frozen {
|
||||||
|
position: relative;
|
||||||
|
@apply rounded-md border shadow-frozen-ring;
|
||||||
|
}
|
||||||
|
.border-ring-frozen::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -2px; /* Adjust based on desired border width */
|
||||||
|
bottom: -2px;
|
||||||
|
left: -2px;
|
||||||
|
right: -2px;
|
||||||
|
/* use the frozen-blue color from tailwind */
|
||||||
|
border-radius: inherit;
|
||||||
|
pointer-events: none;
|
||||||
|
@apply border-2 border-frozen-blue;
|
||||||
|
}
|
||||||
|
.border-frozen {
|
||||||
|
@apply border shadow-frozen-ring;
|
||||||
|
}
|
||||||
|
.frosted {
|
||||||
|
@apply rounded-md bg-frozen-blue backdrop-blur-xs;
|
||||||
|
}
|
||||||
|
.frozen {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.frozen::before,
|
||||||
|
.frozen::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
bottom: -10px;
|
||||||
|
left: -10px;
|
||||||
|
right: -10px;
|
||||||
|
background: rgba(
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
0.5
|
||||||
|
); /* Reduced opacity for better readability */
|
||||||
|
border-radius: 10px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.frozen::before {
|
||||||
|
filter: blur(5px); /* Less blur for better readability */
|
||||||
|
}
|
||||||
|
.frozen::after {
|
||||||
|
filter: blur(10px); /* Less blur for better readability */
|
||||||
|
opacity: 0.2; /* Reduced opacity */
|
||||||
|
}
|
||||||
.extra-side-bar-buttons {
|
.extra-side-bar-buttons {
|
||||||
@apply relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground transition-all duration-500 ease-in-out;
|
@apply relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground transition-all duration-500 ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ module.exports = {
|
||||||
"slow-wiggle": "wiggle 500ms ease-in-out 1",
|
"slow-wiggle": "wiggle 500ms ease-in-out 1",
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
|
"frozen-blue": "rgba(128, 190, 219, 0.86)", // Custom blue color for the frozen effect
|
||||||
|
"frosted-glass": "rgba(255, 255, 255, 0.8)", // Custom frosted glass effect
|
||||||
"component-icon": "var(--component-icon)",
|
"component-icon": "var(--component-icon)",
|
||||||
"flow-icon": "var(--flow-icon)",
|
"flow-icon": "var(--flow-icon)",
|
||||||
"low-indigo": "var(--low-indigo)",
|
"low-indigo": "var(--low-indigo)",
|
||||||
|
|
@ -139,6 +141,13 @@ module.exports = {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ["var(--font-sans)", ...fontFamily.sans],
|
sans: ["var(--font-sans)", ...fontFamily.sans],
|
||||||
},
|
},
|
||||||
|
boxShadow: {
|
||||||
|
"frozen-ring": "0 0 10px 2px rgba(128, 190, 230, 0.5)",
|
||||||
|
"frosted-ring": "0 0 10px 2px rgba(128, 190, 230, 0.7)",
|
||||||
|
},
|
||||||
|
backdropBlur: {
|
||||||
|
xs: "2px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue