diff --git a/packages/app/src/pages/tv/components/controlPanel/index.jsx b/packages/app/src/pages/tv/components/controlPanel/index.jsx
deleted file mode 100755
index 0ad182e4..00000000
--- a/packages/app/src/pages/tv/components/controlPanel/index.jsx
+++ /dev/null
@@ -1,430 +0,0 @@
-import React from "react"
-import * as antd from "antd"
-
-import { Icons, createIconRender } from "components/Icons"
-
-import Livestream from "../../../../models/livestream"
-
-import "./index.less"
-
-const CategoryView = (props) => {
- const category = props.category
-
- const [categoryData, setCategoryData] = React.useState(null)
-
- const loadData = async () => {
- const categoryData = await Livestream.getCategories(category).catch((err) => {
- console.error(err)
-
- app.message.error("Failed to load category")
-
- return null
- })
-
- setCategoryData(categoryData)
- }
-
- React.useEffect(() => {
- if (props.category) {
- loadData()
- }
- }, [props.category])
-
- return
- {
- categoryData?.icon &&
-
- {createIconRender(categoryData.icon)}
-
- }
-
-
- {categoryData?.label ?? "No category"}
-
-
-}
-
-const StreamingKeyView = (props) => {
- const [streamingKeyVisibility, setStreamingKeyVisibility] = React.useState(false)
-
- const toogleVisibility = (to) => {
- setStreamingKeyVisibility(to ?? !streamingKeyVisibility)
- }
-
- return
- {streamingKeyVisibility ?
- <>
-
toogleVisibility()} />
-
- {props.streamingKey ?? "No streaming key available"}
-
- > :
- toogleVisibility()}
- >
-
- Click to show key
-
- }
-
-}
-
-const LivestreamsCategoriesSelector = (props) => {
- const [categories, setCategories] = React.useState([])
- const [loading, setLoading] = React.useState(true)
-
- const loadData = async () => {
- setLoading(true)
-
- const categories = await Livestream.getCategories().catch((err) => {
- console.error(err)
-
- app.message.error("Failed to load categories")
-
- return null
- })
-
- console.log(`Loaded categories >`, categories)
-
- setLoading(false)
-
- if (categories) {
- setCategories(categories)
- }
- }
-
- React.useEffect(() => {
- loadData()
- }, [])
-
- if (loading) {
- return
- }
-
- return props.updateStreamInfo("category", value)}
- >
- {
- categories.map((category) => {
- return {category?.label ?? "No category"}
- })
- }
-
-}
-
-const StreamInfoEditor = (props) => {
- const [streamInfo, setStreamInfo] = React.useState(props.defaultStreamInfo ?? {})
-
- const updateStreamInfo = (key, value) => {
- setStreamInfo({
- ...streamInfo,
- [key]: value,
- })
- }
-
- const saveStreamInfo = async () => {
- if (typeof props.onSave === "function") {
- return await props.onSave(streamInfo)
- }
-
- // peform default save
- const result = await Livestream.updateLivestreamInfo(streamInfo).catch((err) => {
- console.error(err)
-
- app.message.error("Failed to update stream info")
-
- return false
- })
-
- if (result) {
- app.message.success("Stream info updated")
- }
-
- if (typeof props.onSaveComplete === "function") {
- await props.onSaveComplete(result)
- }
-
- return result
- }
-
- return
-
-
- Title
-
-
-
updateStreamInfo("title", e.target.value)}
- />
-
-
-
-
- Description
-
-
-
updateStreamInfo("description", e.target.value)}
- />
-
-
-
-
- Save
-
-
-}
-
-export default (props) => {
- const [streamInfo, setStreamInfo] = React.useState({})
- const [addresses, setAddresses] = React.useState({})
-
- const [isConnected, setIsConnected] = React.useState(false)
- const [streamingKey, setStreamingKey] = React.useState(null)
-
- const onClickEditInfo = () => {
- app.ModalController.open(() => {
- if (result) {
- app.ModalController.close()
-
- fetchStreamInfo()
- }
- }}
- />)
- }
-
- const regenerateStreamingKey = async () => {
- antd.Modal.confirm({
- title: "Regenerate streaming key",
- content: "Are you sure you want to regenerate the streaming key? After this, all other generated keys will be deleted.",
- onOk: async () => {
- const result = await Livestream.regenerateStreamingKey().catch((err) => {
- app.message.error(`Failed to regenerate streaming key`)
- console.error(err)
-
- return null
- })
-
- if (result) {
- setStreamingKey(result.key)
- }
- }
- })
- }
-
- const fetchStreamingKey = async () => {
- const streamingKey = await Livestream.getStreamingKey().catch((err) => {
- console.error(err)
- return false
- })
-
- if (streamingKey) {
- setStreamingKey(streamingKey.key)
- }
- }
-
- const fetchAddresses = async () => {
- const addresses = await Livestream.getAddresses().catch((error) => {
- app.message.error(`Failed to fetch addresses`)
- console.error(error)
-
- return null
- })
-
- if (addresses) {
- setAddresses(addresses)
- }
- }
-
- const fetchStreamInfo = async () => {
- const result = await Livestream.getStreamInfo().catch((err) => {
- console.error(err)
- return false
- })
-
- console.log("Stream info", result)
-
- if (result) {
- setStreamInfo(result)
- }
- }
-
- React.useEffect(() => {
- fetchAddresses()
- fetchStreamInfo()
- fetchStreamingKey()
- }, [])
-
- return
-
-
-

-
-
-
-
-
:
}
- >
- {isConnected ? "Connected" : "Disconnected"}
-
-
-
-
- Title
-
-
- {streamInfo?.title ?? "No title"}
-
-
-
-
-
- Description
-
-
-
- {streamInfo?.description ?? "No description"}
-
-
-
-
-
- Category
-
-
-
-
-
-
-
}
- onClick={onClickEditInfo}
- >
- Edit info
-
-
-
-
-
-
-
Emission
-
-
- Ingestion URL
-
-
- {addresses.ingestURL ?? "No ingest URL available"}
-
-
-
-
-
-
- Streaming key
-
-
-
regenerateStreamingKey()}>
-
- Regenerate
-
-
-
-
-
-
-
-
-
-
-
-
Additional options
-
-
-
-
-
-
-
-
URL Information
-
-
- AAC URL (Only Audio)
-
-
- {addresses.aacURL ?? "No AAC URL available"}
-
-
-
-
- HLS URL
-
-
- {addresses.hlsURL ?? "No HLS URL available"}
-
-
-
-
- FLV URL
-
-
- {addresses.flvURL ?? "No FLV URL available"}
-
-
-
-
-
-
Statistics
-
-
-
-
- Cannot connect with statistics
-
-
-
-
-
-
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/tv/tabs.jsx b/packages/app/src/pages/tv/tabs.jsx
index f1f40c2e..2470b638 100755
--- a/packages/app/src/pages/tv/tabs.jsx
+++ b/packages/app/src/pages/tv/tabs.jsx
@@ -1,5 +1,5 @@
-import FeedTab from "./components/feed"
-import ControlPanelTab from "./components/controlPanel"
+import FeedTab from "./tabs/feed"
+import ControlPanelTab from "./tabs/livestreamControlPanel"
export default [
{
diff --git a/packages/app/src/pages/tv/components/feed/index.jsx b/packages/app/src/pages/tv/tabs/feed/index.jsx
similarity index 100%
rename from packages/app/src/pages/tv/components/feed/index.jsx
rename to packages/app/src/pages/tv/tabs/feed/index.jsx
diff --git a/packages/app/src/pages/tv/components/feed/index.less b/packages/app/src/pages/tv/tabs/feed/index.less
similarity index 100%
rename from packages/app/src/pages/tv/components/feed/index.less
rename to packages/app/src/pages/tv/tabs/feed/index.less
diff --git a/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/CategoriesSelector/index.jsx b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/CategoriesSelector/index.jsx
new file mode 100644
index 00000000..bdf3c73a
--- /dev/null
+++ b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/CategoriesSelector/index.jsx
@@ -0,0 +1,49 @@
+import React from "react"
+import * as antd from "antd"
+
+import Livestream from "models/livestream"
+
+export default (props) => {
+ const [categories, setCategories] = React.useState([])
+ const [loading, setLoading] = React.useState(true)
+
+ const loadData = async () => {
+ setLoading(true)
+
+ const categories = await Livestream.getCategories().catch((err) => {
+ console.error(err)
+
+ app.message.error("Failed to load categories")
+
+ return null
+ })
+
+ console.log(`Loaded categories >`, categories)
+
+ setLoading(false)
+
+ if (categories) {
+ setCategories(categories)
+ }
+ }
+
+ React.useEffect(() => {
+ loadData()
+ }, [])
+
+ if (loading) {
+ return
+ }
+
+ return props.updateStreamInfo("category", value)}
+ >
+ {
+ categories.map((category) => {
+ return {category?.label ?? "No category"}
+ })
+ }
+
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/CategoryViewResolver/index.jsx b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/CategoryViewResolver/index.jsx
new file mode 100644
index 00000000..1a1fc973
--- /dev/null
+++ b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/CategoryViewResolver/index.jsx
@@ -0,0 +1,41 @@
+import React from "react"
+import { createIconRender } from "components/Icons"
+
+import Livestream from "models/livestream"
+
+export default (props) => {
+ const category = props.category
+
+ const [categoryData, setCategoryData] = React.useState(null)
+
+ const loadData = async () => {
+ const categoryData = await Livestream.getCategories(category).catch((err) => {
+ console.error(err)
+
+ app.message.error("Failed to load category")
+
+ return null
+ })
+
+ setCategoryData(categoryData)
+ }
+
+ React.useEffect(() => {
+ if (props.category) {
+ loadData()
+ }
+ }, [props.category])
+
+ return
+ {
+ categoryData?.icon &&
+
+ {createIconRender(categoryData.icon)}
+
+ }
+
+
+ {categoryData?.label ?? "No category"}
+
+
+}
diff --git a/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/ProfileEditor/index.jsx b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/ProfileEditor/index.jsx
new file mode 100644
index 00000000..cafc1a63
--- /dev/null
+++ b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/ProfileEditor/index.jsx
@@ -0,0 +1,143 @@
+import React from "react"
+import * as antd from "antd"
+import { Icons } from "components/Icons"
+import UploadButton from "components/UploadButton"
+
+import CategoriesSelector from "../CategoriesSelector"
+
+import "./index.less"
+
+export default (props) => {
+ const [profileData, setProfileData] = React.useState(props.profileData ?? {})
+
+ const updateStreamInfo = (key, value) => {
+ setProfileData((oldData) => {
+ return {
+ ...oldData,
+ info: {
+ ...oldData.info,
+ [key]: value,
+ }
+ }
+ })
+ }
+
+ const handleClickSave = async () => {
+ if (typeof props.onSave === "function") {
+ return await props.onSave(profileData)
+ }
+ }
+
+ const handleClickDelete = async () => {
+ if (typeof props.onDelete === "function") {
+ return await props.onDelete(profileData._id)
+ }
+ }
+
+ return
+
+
+ Profile Name
+
+
+
+
setProfileData((oldData) => {
+ return {
+ ...oldData,
+ profile_name: e.target.value,
+ }
+ })}
+ />
+
+
+
+
+ Thumbnail
+
+
+
+ {
+ profileData.info?.thumbnail &&

+ }
+
+
+
{
+ console.log(`Uploaded file >`, file)
+
+ updateStreamInfo("thumbnail", file.url)
+ }}
+ />
+
+ {
+ profileData.info?.thumbnail && }
+ onClick={() => updateStreamInfo("thumbnail", null)}
+ danger
+ >
+ Delete
+
+ }
+
+
+
+
+
+ Title
+
+
+
+
updateStreamInfo("title", e.target.value)}
+ />
+
+
+
+
+ Description
+
+
+
+
updateStreamInfo("description", e.target.value)}
+ />
+
+
+
+
+
+ Save
+
+
+
+ Delete
+
+
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/ProfileEditor/index.less b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/ProfileEditor/index.less
new file mode 100644
index 00000000..21f41192
--- /dev/null
+++ b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/ProfileEditor/index.less
@@ -0,0 +1,48 @@
+.profileEditor {
+ display: flex;
+ flex-direction: column;
+
+ .field {
+ display: flex;
+ flex-direction: column;
+
+ margin-bottom: 20px;
+
+ .value {
+ margin-top: 5px;
+ margin-left: 20px;
+
+ .ant-select {
+ min-width: 200px;
+ }
+
+ &.thumbnail {
+ display: flex;
+ flex-direction: column;
+
+ align-items: center;
+ justify-content: center;
+
+ width: 100%;
+
+ gap: 10px;
+
+ img {
+ border-radius: 12px;
+
+ max-width: 600px;
+ max-height: 400px;
+
+ object-fit: cover;
+ }
+
+ .controls {
+ display: flex;
+ flex-direction: row;
+
+ gap: 10px;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/ProfileSelector/index.jsx b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/ProfileSelector/index.jsx
new file mode 100644
index 00000000..56961e1d
--- /dev/null
+++ b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/ProfileSelector/index.jsx
@@ -0,0 +1,65 @@
+import React from "react"
+import * as antd from "antd"
+import { Icons } from "components/Icons"
+//import { useTranslation } from "react-i18next"
+
+const ProfilesDropboxCustomRender = (props) => {
+ return <>
+ {props.menu}
+
+
+
+
+
+ }
+ onClick={props.onClickAdd}
+ >
+ Create
+
+
+ >
+}
+
+export default (props) => {
+ const [inputValue, setInputValue] = React.useState("")
+
+ const onInputChange = (e) => {
+ setInputValue(e.target.value)
+ }
+
+ const onAddNewProfile = async () => {
+ const value = inputValue.trim()
+
+ if (!value) {
+ return
+ }
+
+ setInputValue("")
+
+ props.onCreateProfile(value)
+ }
+
+ return props.onChangeProfile(_id, item)}
+ dropdownRender={(menu) => }
+ options={props.profiles.map((item) => ({
+ label: item.profile_name,
+ value: item._id,
+ }))}
+ />
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/StreamingKeyViewer/index.jsx b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/StreamingKeyViewer/index.jsx
new file mode 100644
index 00000000..147066bb
--- /dev/null
+++ b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/StreamingKeyViewer/index.jsx
@@ -0,0 +1,29 @@
+import React from "react"
+import { Icons } from "components/Icons"
+
+import "./index.less"
+
+export default (props) => {
+ const [streamingKeyVisibility, setStreamingKeyVisibility] = React.useState(false)
+
+ const toogleVisibility = (to) => {
+ setStreamingKeyVisibility(to ?? !streamingKeyVisibility)
+ }
+
+ return
+ {streamingKeyVisibility ?
+ <>
+
toogleVisibility()} />
+
+ {props.streamingKey ?? "No streaming key available"}
+
+ > :
+ toogleVisibility()}
+ >
+
+ Click to show key
+
+ }
+
+}
diff --git a/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/StreamingKeyViewer/index.less b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/StreamingKeyViewer/index.less
new file mode 100644
index 00000000..622f83f8
--- /dev/null
+++ b/packages/app/src/pages/tv/tabs/livestreamControlPanel/components/StreamingKeyViewer/index.less
@@ -0,0 +1,23 @@
+.streamingKeyString {
+ display: inline-flex;
+ flex-direction: row;
+
+ align-items: center;
+ justify-content: center;
+
+ color: var(--text-color);
+
+ svg {
+ font-size: 1.5rem;
+
+ cursor: pointer;
+ }
+
+ div {
+ display: inline-flex;
+ flex-direction: row;
+
+ align-items: center;
+ justify-content: center;
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/tv/tabs/livestreamControlPanel/index.jsx b/packages/app/src/pages/tv/tabs/livestreamControlPanel/index.jsx
new file mode 100755
index 00000000..41438061
--- /dev/null
+++ b/packages/app/src/pages/tv/tabs/livestreamControlPanel/index.jsx
@@ -0,0 +1,344 @@
+import React from "react"
+import * as antd from "antd"
+import { Icons } from "components/Icons"
+
+import ProfileSelector from "./components/ProfileSelector"
+import CategoryViewResolver from "./components/CategoryViewResolver"
+import StreamingKeyViewer from "./components/StreamingKeyViewer"
+import ProfileEditor from "./components/ProfileEditor"
+
+import Livestream from "models/livestream"
+
+import "./index.less"
+
+export default (props) => {
+ const [L_Profiles, R_Profiles, E_Profiles, M_Profiles] = app.cores.api.useRequest(Livestream.getProfiles)
+
+ const [selectedProfileId, setSelectedProfileId] = React.useState(null)
+
+ const [addresses, setAddresses] = React.useState({})
+
+ const [isConnected, setIsConnected] = React.useState(false)
+
+ const fetchAddresses = async () => {
+ const addresses = await Livestream.getAddresses().catch((error) => {
+ app.message.error(`Failed to fetch addresses`)
+ console.error(error)
+
+ return null
+ })
+
+ if (addresses) {
+ setAddresses(addresses)
+ }
+ }
+
+ React.useEffect(() => {
+ if (R_Profiles) {
+ fetchAddresses()
+
+ if (!selectedProfileId) {
+ setSelectedProfileId(R_Profiles[0]?._id)
+ }
+ }
+ }, [R_Profiles])
+
+ if (E_Profiles) {
+ console.error(E_Profiles)
+
+ return
+ }
+
+ if (L_Profiles) {
+ return
+ }
+
+ const profileData = R_Profiles.find((profile) => profile._id === selectedProfileId)
+
+ const handleCreateProfile = async (profile_name) => {
+ if (!profile_name) {
+ return false
+ }
+
+ const result = await Livestream.postProfile({
+ profile_name: profile_name,
+ }).catch((err) => {
+ console.error(err)
+
+ app.message.error("Failed to add new profile")
+
+ return false
+ })
+
+ if (result) {
+ app.message.success("New profile added")
+
+ await M_Profiles()
+
+ setSelectedProfileId(result._id)
+ }
+ }
+
+ const handleCurrentProfileDataUpdate = async (newProfileData) => {
+ if (!profileData) {
+ return
+ }
+
+ const result = await Livestream.postProfile({
+ ...newProfileData,
+ profile_id: profileData._id,
+ }).catch((err) => {
+ console.error(err)
+
+ app.message.error("Failed to update profile")
+
+ return false
+ })
+
+ if (result) {
+ app.message.success("Profile updated")
+
+ app.ModalController.close()
+
+ M_Profiles()
+ }
+ }
+
+ const handleCurrentProfileDelete = async () => {
+ if (!profileData) {
+ return
+ }
+
+ // open confirm modal
+ antd.Modal.confirm({
+ title: "Delete profile",
+ content: "Are you sure you want to delete this profile?",
+ onOk: async () => {
+ const result = await Livestream.deleteProfile(profileData._id).catch((err) => {
+ console.error(err)
+
+ app.message.error("Failed to delete profile")
+
+ return false
+ })
+
+ if (result) {
+ app.message.success("Profile deleted")
+
+ app.ModalController.close()
+
+ setSelectedProfileId(null)
+
+ M_Profiles()
+ }
+ }
+ })
+ }
+
+ const onClickEditInfo = () => {
+ if (!profileData) {
+ return
+ }
+
+ app.ModalController.open(() => )
+ }
+
+ const regenerateStreamingKey = async () => {
+ if (!profileData) {
+ return
+ }
+
+ antd.Modal.confirm({
+ title: "Regenerate streaming key",
+ content: "Are you sure you want to regenerate the streaming key? After this, the old stream key will no longer be valid.",
+ onOk: async () => {
+ const result = await Livestream.regenerateStreamingKey(profileData._id).catch((err) => {
+ app.message.error(`Failed to regenerate streaming key`)
+ console.error(err)
+
+ return null
+ })
+
+ if (result) {
+ M_Profiles()
+ }
+ }
+ })
+ }
+
+ return
+
+
+

+
+
+
+
+
:
}
+ >
+ {isConnected ? "Connected" : "Disconnected"}
+
+
+
+
+
+ Title
+
+
+ {profileData?.info.title ?? "No title"}
+
+
+
+
+
+ Description
+
+
+
+ {profileData?.info.description ?? "No description"}
+
+
+
+
+
+ Category
+
+
+
+
+
+
+
+
{
+ setSelectedProfileId(profileID)
+ }}
+ />
+
+ }
+ onClick={onClickEditInfo}
+ >
+ Edit profile
+
+
+
+
+
+
+
Emission
+
+
+ Ingestion URL
+
+
+ {addresses.ingestURL ?? "No ingest URL available"}
+
+
+
+
+
+
+ Streaming key
+
+
+
regenerateStreamingKey()}>
+
+ Regenerate
+
+
+
+
+
+
+
+
+
+
+
+
Additional options
+
+
+
+
+
+
+
+
URL Information
+
+
+ AAC URL (Only Audio)
+
+
+ {addresses.aacURL ?? "No AAC URL available"}
+
+
+
+
+ HLS URL
+
+
+ {addresses.hlsURL ?? "No HLS URL available"}
+
+
+
+
+ FLV URL
+
+
+ {addresses.flvURL ?? "No FLV URL available"}
+
+
+
+
+
+
Statistics
+
+
+
+
+ Cannot connect with statistics
+
+
+
+
+
+
+}
diff --git a/packages/app/src/pages/tv/components/controlPanel/index.less b/packages/app/src/pages/tv/tabs/livestreamControlPanel/index.less
similarity index 69%
rename from packages/app/src/pages/tv/components/controlPanel/index.less
rename to packages/app/src/pages/tv/tabs/livestreamControlPanel/index.less
index 531f2db5..95fa2dca 100755
--- a/packages/app/src/pages/tv/components/controlPanel/index.less
+++ b/packages/app/src/pages/tv/tabs/livestreamControlPanel/index.less
@@ -6,7 +6,7 @@
transition: all 0.3s ease-in-out;
- .header {
+ .streamingControlPanel_header {
display: flex;
flex-direction: row;
@@ -21,13 +21,20 @@
transition: all 0.3s ease-in-out;
- .preview {
+ .streamingControlPanel_header_thumbnail {
+ align-self: center;
+ justify-self: center;
+
+ border-radius: 12px;
+
height: 100%;
- width: 300px;
+ width: 100%;
img {
- width: 100%;
- height: 100%;
+ height: 15vh;
+ width: 20vw;
+
+ object-fit: cover;
border-radius: 12px;
}
@@ -35,16 +42,32 @@
margin-right: 40px;
}
- .details {
+ .streamingControlPanel_header_details {
display: inline-flex;
flex-direction: column;
padding: 20px 0;
width: 100%;
- .status {
+ .streamingControlPanel_header_details_status {
margin-bottom: 20px;
}
+
+ color: var(--text-color);
+ }
+
+ .streamingControlPanel_header_actions {
+ display: flex;
+ flex-direction: row;
+
+ justify-items: center;
+
+ gap: 10px;
+
+ .ant-select {
+ height: fit-content;
+ min-width: 300px;
+ }
}
}
@@ -56,6 +79,8 @@
transition: all 0.3s ease-in-out;
+ gap: 20px;
+
code {
padding: 5px 8px;
font-size: 1rem;
@@ -76,7 +101,6 @@
align-items: flex-start;
-
h1,
h2,
h3 {
@@ -87,9 +111,9 @@
.title {
display: inline-flex;
flex-direction: row;
-
+
justify-content: space-between;
-
+
width: 100%;
}
@@ -102,49 +126,4 @@
}
}
}
-}
-
-.streamingKeyString {
- display: inline-flex;
- flex-direction: row;
-
- align-items: center;
- justify-content: center;
-
- color: var(--text-color);
-
- svg {
- font-size: 1.5rem;
-
- cursor: pointer;
- }
-
- div {
- display: inline-flex;
- flex-direction: row;
-
- align-items: center;
- justify-content: center;
- }
-}
-
-.streamInfoEditor {
- display: flex;
- flex-direction: column;
-
- .field {
- display: flex;
- flex-direction: column;
-
- margin-bottom: 20px;
-
- .value {
- margin-top: 5px;
- margin-left: 20px;
-
- .ant-select {
- min-width: 200px;
- }
- }
- }
}
\ No newline at end of file