Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front

This commit is contained in:
X1627315083@163.com
2026-03-05 13:51:17 +08:00
11 changed files with 246 additions and 211 deletions

View File

@@ -0,0 +1,5 @@
<svg width="13" height="14" viewBox="0 0 13 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.77778 6.3C5.77778 5.9134 5.45443 5.6 5.05556 5.6C4.65668 5.6 4.33333 5.9134 4.33333 6.3V9.8C4.33333 10.1866 4.65668 10.5 5.05556 10.5C5.45443 10.5 5.77778 10.1866 5.77778 9.8V6.3Z" fill="#FFEAE2"/>
<path d="M8.66667 6.3C8.66667 5.9134 8.34332 5.6 7.94444 5.6C7.54557 5.6 7.22222 5.9134 7.22222 6.3V9.8C7.22222 10.1866 7.54557 10.5 7.94444 10.5C8.34332 10.5 8.66667 10.1866 8.66667 9.8V6.3Z" fill="#FFEAE2"/>
<path d="M6.97498 0C8.3006 0 9.45611 0.874433 9.77762 2.1209L9.95278 2.8H11.5453C11.5515 2.79992 11.5578 2.79992 11.564 2.8H12.2778C12.6767 2.8 13 3.1134 13 3.5C13 3.8866 12.6767 4.2 12.2778 4.2H12.2045L11.3977 11.4983C11.2405 12.9211 10.0017 14 8.5253 14H4.4747C2.99834 14 1.75954 12.9211 1.60225 11.4983L0.79547 4.2H0.722222C0.32335 4.2 0 3.8866 0 3.5C0 3.1134 0.32335 2.8 0.722222 2.8H1.43598C1.44223 2.79992 1.44846 2.79992 1.45467 2.8H3.04722L3.22238 2.1209C3.54389 0.874431 4.6994 0 6.02502 0H6.97498ZM9.40399 4.2C9.39353 4.20022 9.3831 4.20022 9.37269 4.2H3.6273C3.6169 4.20022 3.60647 4.20022 3.59601 4.2H2.24818L3.03848 11.3491C3.11712 12.0605 3.73652 12.6 4.4747 12.6H8.5253C9.26348 12.6 9.88288 12.0605 9.96152 11.3491L10.7518 4.2H9.40399ZM4.62369 2.46045L4.53611 2.8H8.46387L8.37629 2.46045C8.21554 1.83722 7.63778 1.4 6.97497 1.4H6.025C5.3622 1.4 4.78444 1.83722 4.62369 2.46045Z" fill="#FFEAE2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -30,7 +30,7 @@
<script setup lang="ts"> <script setup lang="ts">
import flowCanvas from './FlowCanvas/flow-canvas.vue' import flowCanvas from './FlowCanvas/flow-canvas.vue'
import card from './FlowCanvas/components/cards/index.vue' import card from './FlowCanvas/components/nodes/cards/index.vue'
import { computed, ref, markRaw, onMounted, reactive, nextTick } from 'vue' import { computed, ref, markRaw, onMounted, reactive, nextTick } from 'vue'
const data = reactive({ const data = reactive({
x: 100, x: 100,

View File

@@ -8,7 +8,7 @@
:position="handle.position" :position="handle.position"
:connectable="false" :connectable="false"
/> />
<div class="item"> <div class="item" @mousedown="(e) => stateManager.setActiveNodeID(node.id)">
<slot></slot> <slot></slot>
</div> </div>
<div class="add" @mousedown.stop v-if="isAdd" @click="onAdd"> <div class="add" @mousedown.stop v-if="isAdd" @click="onAdd">

View File

@@ -156,7 +156,7 @@
<style lang="less" scoped> <style lang="less" scoped>
.card { .card {
width: 250px; width: 244px;
height: auto; height: auto;
--border-radius: 16px; --border-radius: 16px;
border-radius: var(--border-radius); border-radius: var(--border-radius);
@@ -164,6 +164,9 @@
box-shadow: 0 15px 21px 0 rgba(0, 0, 0, 0.05); box-shadow: 0 15px 21px 0 rgba(0, 0, 0, 0.05);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
&.active {
box-shadow: 0 15px 21px 0 rgba(0, 0, 0, 0.05), 0 0 0 2px #fae5d8;
}
> .header { > .header {
border-radius: var(--border-radius) var(--border-radius) 0 0; border-radius: var(--border-radius) var(--border-radius) 0 0;
height: 50px; height: 50px;
@@ -173,7 +176,7 @@
padding: 0 16px; padding: 0 16px;
position: relative; position: relative;
user-select: none; user-select: none;
> .delete-icon{ > .delete-icon {
margin-left: auto; margin-left: auto;
cursor: pointer; cursor: pointer;
} }

View File

@@ -1,17 +1,11 @@
<template> <template>
<!-- 结果图片 --> <!-- 结果图片 -->
<div <div class="text" @mousedown="onMouseDown">
class="text"
:class="{ edit }"
@click="onClick"
@mousedown="onMouseDown"
@dblclick="onDoubleClick"
>
<div <div
tabindex="0" tabindex="0"
class="input" class="input"
ref="inputRef" ref="inputRef"
:contenteditable="edit" :contenteditable="active"
@input="onInput" @input="onInput"
@blur="onBlur" @blur="onBlur"
@paste.prevent @paste.prevent
@@ -20,8 +14,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, onMounted, nextTick } from 'vue' import { reactive, ref, onMounted, nextTick, watch } from 'vue'
const props = defineProps({ const props = defineProps({
active: {
type: Boolean,
default: false
},
data: { data: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
@@ -31,7 +29,6 @@
const data = reactive({ const data = reactive({
text: props.data?.text || '点击编辑文本' text: props.data?.text || '点击编辑文本'
}) })
const edit = ref(false)
const time = ref(null) const time = ref(null)
const inputRef = ref<any>() const inputRef = ref<any>()
const onInput = () => { const onInput = () => {
@@ -39,27 +36,26 @@
data.text = text data.text = text
emit('update-data', data) emit('update-data', data)
} }
const onBlur = () => { // watch(
edit.value = false // () => props.active,
} // (newVal) => {
const onClick = () => { // if (!newVal) return
if (edit.value) return // nextTick(() => {
edit.value = true // // 光标定位到文本末尾
nextTick(() => { // const range = document.createRange()
// 光标定位到文本末尾 // const selection = window.getSelection()
const range = document.createRange() // range.selectNodeContents(inputRef.value)
const selection = window.getSelection() // range.collapse(false)
range.selectNodeContents(inputRef.value) // selection.removeAllRanges()
range.collapse(false) // selection.addRange(range)
selection.removeAllRanges() // })
selection.addRange(range) // }
}) // )
}
const onDoubleClick = () => {
clearTimeout(time.value)
}
const onMouseDown = (e: MouseEvent) => { const onMouseDown = (e: MouseEvent) => {
if (edit.value) e.stopPropagation() if (props.active) e.stopPropagation()
}
const onBlur = () => {
// emit('update-data', data)
} }
onMounted(() => { onMounted(() => {
inputRef.value.innerHTML = data.text inputRef.value.innerHTML = data.text
@@ -73,7 +69,7 @@
user-select: none; user-select: none;
border: 1px solid transparent; border: 1px solid transparent;
padding: 2px; padding: 2px;
&.edit { &.active {
border-color: #000; border-color: #000;
> .input { > .input {
cursor: text; cursor: text;

View File

@@ -27,6 +27,8 @@
> >
<component <component
:is="components[node.data.component]" :is="components[node.data.component]"
:class="{ active: stateManager.activeNodeID.value === node.id }"
:active="stateManager.activeNodeID.value === node.id"
:node="node" :node="node"
:config="node.data" :config="node.data"
:data="node.data.data" :data="node.data.data"
@@ -127,7 +129,11 @@
stateManager.nodes.value = layout( stateManager.nodes.value = layout(
stateManager.nodes.value, stateManager.nodes.value,
stateManager.edges.value, stateManager.edges.value,
direction direction,
{
nodesep: nodeManager.nodesep,
ranksep: nodeManager.ranksep
}
) )
nextTick(() => { nextTick(() => {
fitView({ maxZoom: 1 }) fitView({ maxZoom: 1 })
@@ -187,6 +193,15 @@
} }
} }
}) })
nodeManager.createResultNode({
data: {
disableDelete: true,
isHeader: false,
data: {
url: props.config.url
}
}
})
}) })
</script> </script>
<style lang="less"> <style lang="less">

View File

@@ -26,6 +26,7 @@ export class EventManager {
} }
/** 处理点击 */ /** 处理点击 */
handleClick(event: any) { handleClick(event: any) {
this.stateManager.setActiveNodeID("")
const tool = this.stateManager.tool.value const tool = this.stateManager.tool.value
if (tool === TOOLS.TEXT) { if (tool === TOOLS.TEXT) {
const { x, y, zoom } = this.vueFlow.value.viewport const { x, y, zoom } = this.vueFlow.value.viewport

View File

@@ -12,6 +12,8 @@ interface NodeOptions {
export class NodeManager { export class NodeManager {
stateManager: any stateManager: any
vueFlow: any vueFlow: any
nodesep = 100 // 节点间距
ranksep = 100 // 层级间距
constructor(options) { constructor(options) {
this.stateManager = options.stateManager; this.stateManager = options.stateManager;
this.vueFlow = options.vueFlow this.vueFlow = options.vueFlow
@@ -37,7 +39,7 @@ export class NodeManager {
(!snode ? (!snode ?
{ x: positionX, y: positionY } : { x: positionX, y: positionY } :
{ {
x: snode.position.x + snode.dimensions.width + 50 + positionX, x: snode.position.x + snode.dimensions.width + this.nodesep + positionX,
y: snode.position.y + positionY y: snode.position.y + positionY
}) })
const data = options?.data || {} const data = options?.data || {}
@@ -100,18 +102,18 @@ export class NodeManager {
} }
return this.createNode(options_) return this.createNode(options_)
} }
copyNodeById(id: string) { copyNodeById(id: string) {
const node = this.stateManager.getNodeById(id) const node = this.stateManager.getNodeById(id)
const flowNode = this.stateManager.flowManager.getNodeById(id) const flowNode = this.stateManager.flowManager.getNodeById(id)
console.log(node,this.stateManager.flowManager.getNodeById(id)) console.log(node, this.stateManager.flowManager.getNodeById(id))
if (!node) return console.warn(`copyNodeById: ${id}找不到对应节点`) if (!node) return console.warn(`copyNodeById: ${id}找不到对应节点`)
const node_ = { const node_ = {
...JSON.parse(JSON.stringify(node)), ...JSON.parse(JSON.stringify(node)),
id: createId(), id: createId(),
position: { position: {
x: node.position.x, x: node.position.x,
y: node.position.y + (flowNode?.dimensions?.height || 0) + 50, y: node.position.y + (flowNode?.dimensions?.height || 0) + this.ranksep,
} }
} }
delete node_.data?.superiorID delete node_.data?.superiorID

View File

@@ -10,6 +10,7 @@ export interface NodesItem {
} }
export class StateManager { export class StateManager {
vueFlow: any vueFlow: any
activeNodeID: any
nodes: any nodes: any
nodes_: any nodes_: any
edges: any edges: any
@@ -49,6 +50,7 @@ export class StateManager {
this.historyList = ref([]) this.historyList = ref([])
this.historyIndex = ref(0) this.historyIndex = ref(0)
this.activeNodeID = ref("")
this.nodes = ref<NodesItem[]>([]); this.nodes = ref<NodesItem[]>([]);
this.nodes_ = computed(() => { this.nodes_ = computed(() => {
return this.nodes.value.map((node, index) => { return this.nodes.value.map((node, index) => {
@@ -90,6 +92,8 @@ export class StateManager {
}) })
} }
/** 设置激活节点 */
setActiveNodeID(id: string) { this.activeNodeID.value = id }
/** 添加节点 */ /** 添加节点 */
addNode(node: NodesItem) { addNode(node: NodesItem) {
this.nodes.value.push(node); this.nodes.value.push(node);

View File

@@ -14,7 +14,9 @@ export function useLayout() {
const previousDirection = ref('LR') const previousDirection = ref('LR')
function layout(nodes, edges, direction = 'LR') { function layout(nodes, edges, direction = 'LR', options) {
const nodesep = options?.nodesep || 50 // 节点间距
const ranksep = options?.ranksep || 50 // 层级间距
// 验证和规范化方向参数 // 验证和规范化方向参数
const validDirections = ['TB', 'BT', 'LR', 'RL'] const validDirections = ['TB', 'BT', 'LR', 'RL']
const layoutDirection = validDirections.includes(direction) ? direction : 'LR' const layoutDirection = validDirections.includes(direction) ? direction : 'LR'
@@ -28,7 +30,11 @@ export function useLayout() {
// 根据方向判断是否为水平布局 // 根据方向判断是否为水平布局
const isHorizontal = layoutDirection === 'LR' || layoutDirection === 'RL' const isHorizontal = layoutDirection === 'LR' || layoutDirection === 'RL'
dagreGraph.setGraph({ rankdir: layoutDirection }) dagreGraph.setGraph({
rankdir: layoutDirection,
nodesep,
ranksep,
})
previousDirection.value = layoutDirection previousDirection.value = layoutDirection
@@ -47,7 +53,7 @@ export function useLayout() {
} }
dagre.layout(dagreGraph) dagre.layout(dagreGraph)
// set nodes with updated positions // set nodes with updated positions
return nodes.map((node) => { return nodes.map((node) => {
const nodeWithPosition = dagreGraph.node(node.id) const nodeWithPosition = dagreGraph.node(node.id)
@@ -125,20 +131,20 @@ export function findAndAddChild(items, targetId, newChild) {
*/ */
export function findAndRemoveChild(items, targetId) { export function findAndRemoveChild(items, targetId) {
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
const item = items[i] const item = items[i]
// 如果找到目标节点,从当前数组中删除 // 如果找到目标节点,从当前数组中删除
if (item.id === targetId) { if (item.id === targetId) {
items.splice(i, 1) items.splice(i, 1)
return true return true
} }
// 递归搜索子节点 // 递归搜索子节点
if (item.children && item.children.length > 0) { if (item.children && item.children.length > 0) {
const found = findAndRemoveChild(item.children, targetId) const found = findAndRemoveChild(item.children, targetId)
if (found) return true if (found) return true
} }
} }
return false return false
} }

View File

@@ -1,174 +1,179 @@
<template> <template>
<div class="assist-input-wrapper flex flex-col" :class="{ agent: isAgentMode }"> <div class="assist-input-wrapper flex flex-col" :class="{ agent: isAgentMode }">
<div class="scroll-content flex-col"> <div class="animate-container flex-1 flex flex-col">
<!-- 图片预览区域 --> <div class="scroll-content flex-col">
<div v-if="uploadedImages.length > 0" class="image-preview-list flex wrap"> <!-- 图片预览区域 -->
<div <div v-if="uploadedImages.length > 0" class="image-preview-list flex wrap">
v-for="(image, index) in uploadedImages" <div
:key="index" v-for="(image, index) in uploadedImages"
class="image-preview-item" :key="index"
> class="image-preview-item"
<img :src="image.url" :alt="image.name" class="preview-image" /> >
<div class="image-remove-btn" @click="removeImage(index)"> <img :src="image.url" :alt="image.name" class="preview-image" />
<SvgIcon name="delete" size="16" /> <div class="image-remove-btn" @click="removeImage(index)">
<SvgIcon name="delete" size="16" />
</div>
</div> </div>
</div> </div>
<!-- 编辑区域 -->
<div
ref="editorRef"
class="editor"
contenteditable="true"
:placeholder="$t('Input.placeholder')"
@input="handleEditorInput"
@paste="handleEditorPaste"
@keydown="handleKeyDown"
></div>
</div> </div>
<!-- 编辑区域 --> <div class="operate flex align-center space-between">
<div <div class="left flex align-center">
ref="editorRef" <div class="attach flex flex-center" @click="triggerFileUpload">
class="editor" <img src="@/assets/icons/attach.svg" alt="" />
contenteditable="true" </div>
:placeholder="$t('Input.placeholder')" <input
@input="handleEditorInput" ref="fileInputRef"
@paste="handleEditorPaste" type="file"
@keydown="handleKeyDown" accept="image/*"
></div> style="display: none"
</div> @change="handleFileChange"
<div class="operate flex align-center space-between">
<div class="left flex align-center">
<div class="attach flex flex-center" @click="triggerFileUpload">
<img src="@/assets/icons/attach.svg" alt="" />
</div>
<input
ref="fileInputRef"
type="file"
accept="image/*"
style="display: none"
@change="handleFileChange"
/>
<el-select
v-if="!isAgentMode"
v-model="typeValue"
:placeholder="$t('Input.typePlaceholder')"
>
<el-option
v-for="item in typeOptions"
class="input-option"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/> />
</el-select>
<el-select
v-if="!isAgentMode"
v-model="areaValue"
:placeholder="$t('Input.areaPlaceholder')"
>
<el-option
v-for="item in areaOptions"
class="input-option"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
<div v-if="!isAgentMode" class="fida-style-select-wrapper">
<el-select <el-select
v-model="styleValue" v-if="!isAgentMode"
:placeholder="$t('Input.stylePlaceholder')" v-model="typeValue"
@focus="openStylePopup" :placeholder="$t('Input.typePlaceholder')"
/> >
<el-option
v-for="item in typeOptions"
class="input-option"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
<el-select
v-if="!isAgentMode"
v-model="areaValue"
:placeholder="$t('Input.areaPlaceholder')"
>
<el-option
v-for="item in areaOptions"
class="input-option"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
<div v-if="!isAgentMode" class="fida-style-select-wrapper">
<el-select
v-model="styleValue"
:placeholder="$t('Input.stylePlaceholder')"
@focus="openStylePopup"
/>
<el-popover
v-model:visible="stylePopupVisible"
placement="top"
:width="342"
:show-arrow="false"
trigger="click"
popper-class="fida-style-select-popover"
>
<template #reference>
<div class="fida-style-select-trigger"></div>
</template>
<div class="fida-style-popover-content flex flex-col">
<div class="fida-style-popover-header">
{{ $t('Input.chooseStyle') }}
</div>
<div class="fida-style-popover-grid">
<div
v-for="item in styleOptions"
:key="item.value"
class="fida-style-popover-item flex flex-center"
:class="{ 'is-selected': tempSelectedValue === item.value }"
@click="selectStyle(item.value)"
>
<img
:src="getStyleImage(typeValue, item.value)"
class="style-bg"
/>
<span class="fida-option-label flex flex-center">{{
item.label
}}</span>
<img
v-show="tempSelectedValue === item.value"
src="@/assets/images/checked.png"
class="checked-item-icon"
/>
</div>
</div>
<div class="fida-style-popover-footer flex flex-center">
<button class="fida-confirm-btn" @click="confirmStyle">
{{ $t('Input.confirm') }}
</button>
</div>
</div>
</el-popover>
</div>
<el-popover <el-popover
v-model:visible="stylePopupVisible" v-model:visible="settingPopupVisible"
placement="top" placement="top"
:width="342" :width="342"
:show-arrow="false" :show-arrow="false"
trigger="click" trigger="click"
popper-class="fida-style-select-popover" popper-class="fida-setting-popover"
> >
<template #reference> <template #reference>
<div class="fida-style-select-trigger"></div> <img src="@/assets/images/setting.png" class="setting-icon" />
</template> </template>
<div class="fida-style-popover-content flex flex-col"> <div class="fida-setting-popover-content flex flex-col">
<div class="fida-style-popover-header"> <div class="fida-setting-popover-header">
{{ $t('Input.chooseStyle') }} {{ $t('Input.styleTitle') }}
</div> </div>
<div class="fida-style-popover-grid"> <div class="fida-setting-slider-list">
<div <div
v-for="item in styleOptions" v-for="item in settingOptions"
:key="item.value" :key="item.label"
class="fida-style-popover-item flex flex-center" class="fida-setting-slider-item"
:class="{ 'is-selected': tempSelectedValue === item.value }"
@click="selectStyle(item.value)"
> >
<img <div class="fida-slider-label">{{ $t(item.label) }}</div>
:src="getStyleImage(typeValue, item.value)" <div class="fida-slider-row flex align-center">
class="style-bg" <el-slider
/> class="setting-popover-slider"
<span class="fida-option-label flex flex-center">{{ v-model="item.value"
item.label :show-tooltip="false"
}}</span> />
<img <span class="fida-slider-value">{{ item.value }}%</span>
v-show="tempSelectedValue === item.value" </div>
src="@/assets/images/checked.png"
class="checked-item-icon"
/>
</div> </div>
</div> </div>
<div class="fida-style-popover-footer flex flex-center">
<button class="fida-confirm-btn" @click="confirmStyle">
{{ $t('Input.confirm') }}
</button>
</div>
</div> </div>
</el-popover> </el-popover>
</div> </div>
<el-popover <div class="right">
v-model:visible="settingPopupVisible" <div
placement="top" class="create-btn flex flex-center"
:width="342" v-if="!isAgentMode"
:show-arrow="false" @click="handleCreateProject"
trigger="click" >
popper-class="fida-setting-popover" <img src="@/assets/images/shining.png" class="shining-icon" alt="" />
> <span class="create-btn-text">{{ $t('Input.createProject') }}</span>
<template #reference>
<img src="@/assets/images/setting.png" class="setting-icon" />
</template>
<div class="fida-setting-popover-content flex flex-col">
<div class="fida-setting-popover-header">{{ $t('Input.styleTitle') }}</div>
<div class="fida-setting-slider-list">
<div
v-for="item in settingOptions"
:key="item.label"
class="fida-setting-slider-item"
>
<div class="fida-slider-label">{{ $t(item.label) }}</div>
<div class="fida-slider-row flex align-center">
<el-slider
class="setting-popover-slider"
v-model="item.value"
:show-tooltip="false"
/>
<span class="fida-slider-value">{{ item.value }}%</span>
</div>
</div>
</div>
</div> </div>
</el-popover>
</div>
<div class="right">
<div
class="create-btn flex flex-center"
v-if="!isAgentMode"
@click="handleCreateProject"
>
<img src="@/assets/images/shining.png" class="shining-icon" alt="" />
<span class="create-btn-text">{{ $t('Input.createProject') }}</span>
</div>
<div v-else class="sender-btn flex flex-center" @click="handleSendAgent"> <div v-else class="sender-btn flex flex-center" @click="handleSendAgent">
<img <img
v-show="!generating" v-show="!generating"
src="@/assets/images/sender.png" src="@/assets/images/sender.png"
alt="" alt=""
class="sender-icon" class="sender-icon"
/> />
<div v-show="generating" class="sender-pause" /> <div v-show="generating" class="sender-pause" />
</div>
</div> </div>
</div> </div>
</div> </div>
<div v-if="!isAgentMode" class="report-btn flex flex-center" @click="toogltReportTag"> <div v-if="!isAgentMode" class="report-btn flex flex-center" @click="toogltReportTag">
<SvgIcon class="light-icon" name="light" size="16" /> <SvgIcon class="light-icon" name="light" size="16" />
<span>{{ $t('Input.trendingReport') }}</span> <span>{{ $t('Input.trendingReport') }}</span>
@@ -618,24 +623,22 @@
min-height: 23.5rem; min-height: 23.5rem;
max-height: 43.5rem; max-height: 43.5rem;
width: 106.3rem; width: 106.3rem;
border-radius: 2.8rem;
background-color: #fff;
border: 0.1rem solid #00000005;
box-shadow: 0px 0.5rem 1.4rem 0px #0000001a;
margin: 0 auto; margin: 0 auto;
padding: 0; padding: 0;
position: relative; position: relative;
&:not(.agent):hover { &:not(.agent) .animate-container {
box-shadow: 0px 0.5rem 3.36rem 2.2rem #f1ede999; box-shadow: 0px 0.5rem 1.4rem 0px #0000001a;
transition: all 0.3s ease; transition: all 0.3s ease;
top: -1rem; border-radius: 2.8rem;
background-color: #fff;
.report-btn { border: 0.1rem solid #00000005;
bottom: -8.4rem; &:not(.agent):hover {
box-shadow: 0px 0.5rem 3.36rem 2.2rem #f1ede999;
transform: translateY(-1rem);
} }
} }
.scroll-content { .scroll-content {
display: flex; display: flex;
flex: 1; flex: 1;