enhanced mobile view
This commit is contained in:
parent
9c1b9676c8
commit
7e45b6a638
10 changed files with 489 additions and 187 deletions
|
|
@ -1,16 +1,21 @@
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 100vh;
|
min-height: 100dvh;
|
||||||
|
|
||||||
font-family: "DM Sans", sans-serif;
|
font-family: "DM Sans", sans-serif;
|
||||||
color: var(--color-bg);
|
color: var(--color-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@supports not (min-height: 100dvh) {
|
||||||
|
.container {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mainContent {
|
.mainContent {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 3rem;
|
padding: 2.5rem 2rem;
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,39 +23,28 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoText {
|
.logo {
|
||||||
|
|
||||||
font-size: 35px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
letter-spacing: -0.5px;
|
width: 100%;
|
||||||
font-weight: 200;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoPower {
|
.logoImage {
|
||||||
color: var(--color-text);
|
height: 44px;
|
||||||
}
|
width: auto;
|
||||||
|
object-fit: contain;
|
||||||
.logoOn {
|
|
||||||
color: var(--color-secondary);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo img {
|
|
||||||
height: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loginBox {
|
.loginBox {
|
||||||
|
|
||||||
|
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
width: 25%;
|
width: min(100%, 460px);
|
||||||
height: auto;
|
height: auto;
|
||||||
|
margin-top: 2rem;
|
||||||
margin-top: 5%;
|
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
|
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
|
|
@ -297,3 +291,41 @@ button:disabled {
|
||||||
.passwordResetLink .textButton:hover {
|
.passwordResetLink .textButton:hover {
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.mainContent {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoImage {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginBox {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
padding: 1.25rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.registerLink {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 420px) {
|
||||||
|
.mainContent {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginBox {
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input,
|
||||||
|
.button {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,10 +108,11 @@ function Login() {
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.mainContent}>
|
<div className={styles.mainContent}>
|
||||||
<div className={styles.logo}>
|
<div className={styles.logo}>
|
||||||
<div className={styles.logoText}>
|
<img
|
||||||
<span className={styles.logoPower}>Power</span>
|
src="/logos/poweron-logo.png"
|
||||||
<span className={styles.logoOn}>On</span>
|
alt="PowerOn"
|
||||||
</div>
|
className={styles.logoImage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.loginSection}>
|
<div className={styles.loginSection}>
|
||||||
<div className={styles.loginBox}>
|
<div className={styles.loginBox}>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,21 @@
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 100vh;
|
min-height: 100dvh;
|
||||||
|
|
||||||
font-family: "DM Sans", sans-serif;
|
font-family: "DM Sans", sans-serif;
|
||||||
color: var(--color-bg);
|
color: var(--color-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@supports not (min-height: 100dvh) {
|
||||||
|
.container {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mainContent {
|
.mainContent {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 3rem;
|
padding: 2.5rem 2rem;
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,39 +23,28 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoText {
|
.logo {
|
||||||
|
|
||||||
font-size: 35px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
letter-spacing: -0.5px;
|
width: 100%;
|
||||||
font-weight: 200;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoPower {
|
.logoImage {
|
||||||
color: var(--color-text);
|
height: 44px;
|
||||||
}
|
width: auto;
|
||||||
|
object-fit: contain;
|
||||||
.logoOn {
|
|
||||||
color: var(--color-secondary);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo img {
|
|
||||||
height: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loginBox {
|
.loginBox {
|
||||||
|
|
||||||
|
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
width: 25%;
|
width: min(100%, 460px);
|
||||||
height: auto;
|
height: auto;
|
||||||
|
margin-top: 2rem;
|
||||||
margin-top: 5%;
|
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
|
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
|
|
@ -240,3 +234,41 @@ button:disabled {
|
||||||
.infoMessage p {
|
.infoMessage p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.mainContent {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoImage {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginBox {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
padding: 1.25rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.registerLink {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 420px) {
|
||||||
|
.mainContent {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginBox {
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input,
|
||||||
|
.button {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,11 @@ function PasswordResetRequest() {
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.mainContent}>
|
<div className={styles.mainContent}>
|
||||||
<div className={styles.logo}>
|
<div className={styles.logo}>
|
||||||
<div className={styles.logoText}>
|
<img
|
||||||
<span className={styles.logoPower}>Power</span>
|
src="/logos/poweron-logo.png"
|
||||||
<span className={styles.logoOn}>On</span>
|
alt="PowerOn"
|
||||||
</div>
|
className={styles.logoImage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.loginSection}>
|
<div className={styles.loginSection}>
|
||||||
<div className={styles.loginBox}>
|
<div className={styles.loginBox}>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,21 @@
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 100vh;
|
min-height: 100dvh;
|
||||||
|
|
||||||
font-family: "DM Sans", sans-serif;
|
font-family: "DM Sans", sans-serif;
|
||||||
color: var(--color-bg);
|
color: var(--color-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@supports not (min-height: 100dvh) {
|
||||||
|
.container {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mainContent {
|
.mainContent {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 3rem;
|
padding: 2.5rem 2rem;
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,39 +23,28 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoText {
|
.logo {
|
||||||
|
|
||||||
font-size: 35px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
letter-spacing: -0.5px;
|
width: 100%;
|
||||||
font-weight: 200;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoPower {
|
.logoImage {
|
||||||
color: var(--color-text);
|
height: 44px;
|
||||||
}
|
width: auto;
|
||||||
|
object-fit: contain;
|
||||||
.logoOn {
|
|
||||||
color: var(--color-secondary);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo img {
|
|
||||||
height: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loginBox {
|
.loginBox {
|
||||||
|
|
||||||
|
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
width: 25%;
|
width: min(100%, 460px);
|
||||||
height: auto;
|
height: auto;
|
||||||
|
margin-top: 2rem;
|
||||||
margin-top: 5%;
|
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
|
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
|
|
@ -273,3 +267,41 @@ button:disabled {
|
||||||
.infoMessage p {
|
.infoMessage p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.mainContent {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoImage {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginBox {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
padding: 1.25rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.registerLink {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 420px) {
|
||||||
|
.mainContent {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginBox {
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input,
|
||||||
|
.button {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,10 +137,11 @@ function Register() {
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.mainContent}>
|
<div className={styles.mainContent}>
|
||||||
<div className={styles.logo}>
|
<div className={styles.logo}>
|
||||||
<div className={styles.logoText}>
|
<img
|
||||||
<span className={styles.logoPower}>Power</span>
|
src="/logos/poweron-logo.png"
|
||||||
<span className={styles.logoOn}>On</span>
|
alt="PowerOn"
|
||||||
</div>
|
className={styles.logoImage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.loginSection}>
|
<div className={styles.loginSection}>
|
||||||
<div className={styles.loginBox}>
|
<div className={styles.loginBox}>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,21 @@
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 100vh;
|
min-height: 100dvh;
|
||||||
|
|
||||||
font-family: "DM Sans", sans-serif;
|
font-family: "DM Sans", sans-serif;
|
||||||
color: var(--color-bg);
|
color: var(--color-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@supports not (min-height: 100dvh) {
|
||||||
|
.container {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mainContent {
|
.mainContent {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 3rem;
|
padding: 2.5rem 2rem;
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,39 +23,28 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoText {
|
.logo {
|
||||||
|
|
||||||
font-size: 35px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
letter-spacing: -0.5px;
|
width: 100%;
|
||||||
font-weight: 200;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoPower {
|
.logoImage {
|
||||||
color: var(--color-text);
|
height: 44px;
|
||||||
}
|
width: auto;
|
||||||
|
object-fit: contain;
|
||||||
.logoOn {
|
|
||||||
color: var(--color-secondary);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo img {
|
|
||||||
height: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loginBox {
|
.loginBox {
|
||||||
|
|
||||||
|
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
width: 25%;
|
width: min(100%, 460px);
|
||||||
height: auto;
|
height: auto;
|
||||||
|
margin-top: 2rem;
|
||||||
margin-top: 5%;
|
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
|
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
|
|
@ -234,3 +228,41 @@ button:disabled {
|
||||||
font-family: var(--font-family);
|
font-family: var(--font-family);
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.mainContent {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoImage {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginBox {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
padding: 1.25rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.registerLink {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 420px) {
|
||||||
|
.mainContent {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginBox {
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input,
|
||||||
|
.button {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,10 +96,11 @@ function Reset() {
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.mainContent}>
|
<div className={styles.mainContent}>
|
||||||
<div className={styles.logo}>
|
<div className={styles.logo}>
|
||||||
<div className={styles.logoText}>
|
<img
|
||||||
<span className={styles.logoPower}>Power</span>
|
src="/logos/poweron-logo.png"
|
||||||
<span className={styles.logoOn}>On</span>
|
alt="PowerOn"
|
||||||
</div>
|
className={styles.logoImage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.loginSection}>
|
<div className={styles.loginSection}>
|
||||||
<div className={styles.loginBox}>
|
<div className={styles.loginBox}>
|
||||||
|
|
@ -135,10 +136,11 @@ function Reset() {
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.mainContent}>
|
<div className={styles.mainContent}>
|
||||||
<div className={styles.logo}>
|
<div className={styles.logo}>
|
||||||
<div className={styles.logoText}>
|
<img
|
||||||
<span className={styles.logoPower}>Power</span>
|
src="/logos/poweron-logo.png"
|
||||||
<span className={styles.logoOn}>On</span>
|
alt="PowerOn"
|
||||||
</div>
|
className={styles.logoImage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.loginSection}>
|
<div className={styles.loginSection}>
|
||||||
<div className={styles.loginBox}>
|
<div className={styles.loginBox}>
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ interface WorkspaceInputProps {
|
||||||
uploading?: boolean;
|
uploading?: boolean;
|
||||||
selectedProviders?: string[];
|
selectedProviders?: string[];
|
||||||
onProvidersChange?: (providers: string[]) => void;
|
onProvidersChange?: (providers: string[]) => void;
|
||||||
|
isMobile?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
|
|
@ -59,6 +60,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
uploading = false,
|
uploading = false,
|
||||||
selectedProviders = [],
|
selectedProviders = [],
|
||||||
onProvidersChange,
|
onProvidersChange,
|
||||||
|
isMobile = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [prompt, setPrompt] = useState('');
|
const [prompt, setPrompt] = useState('');
|
||||||
const [showAutocomplete, setShowAutocomplete] = useState(false);
|
const [showAutocomplete, setShowAutocomplete] = useState(false);
|
||||||
|
|
@ -267,6 +269,8 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const hasAttachments = attachedFileIds.length > 0 || attachedDataSourceIds.length > 0;
|
const hasAttachments = attachedFileIds.length > 0 || attachedDataSourceIds.length > 0;
|
||||||
|
const _horizontalPadding = isMobile ? 12 : 24;
|
||||||
|
const _controlSize = isMobile ? 38 : 40;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
|
|
@ -277,7 +281,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
{/* Pending uploaded files */}
|
{/* Pending uploaded files */}
|
||||||
{pendingFiles.length > 0 && (
|
{pendingFiles.length > 0 && (
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: '6px 24px',
|
padding: `6px ${_horizontalPadding}px`,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: 6,
|
gap: 6,
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
|
|
@ -314,7 +318,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
{/* Attachment bar */}
|
{/* Attachment bar */}
|
||||||
{hasAttachments && (
|
{hasAttachments && (
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: '6px 24px',
|
padding: `6px ${_horizontalPadding}px`,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: 6,
|
gap: 6,
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
|
|
@ -377,8 +381,8 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: '100%',
|
bottom: '100%',
|
||||||
left: 24,
|
left: _horizontalPadding,
|
||||||
right: 24,
|
right: _horizontalPadding,
|
||||||
maxHeight: 200,
|
maxHeight: 200,
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
background: '#fff',
|
background: '#fff',
|
||||||
|
|
@ -410,7 +414,13 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Main input row */}
|
{/* Main input row */}
|
||||||
<div style={{ padding: '8px 24px 12px', display: 'flex', gap: 8, alignItems: 'flex-end' }}>
|
<div style={{
|
||||||
|
padding: `8px ${_horizontalPadding}px 12px`,
|
||||||
|
display: 'flex',
|
||||||
|
gap: 8,
|
||||||
|
alignItems: isMobile ? 'stretch' : 'flex-end',
|
||||||
|
flexWrap: isMobile ? 'wrap' : 'nowrap',
|
||||||
|
}}>
|
||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
value={prompt}
|
value={prompt}
|
||||||
|
|
@ -420,7 +430,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
disabled={isProcessing}
|
disabled={isProcessing}
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minHeight: 40,
|
minHeight: isMobile ? 44 : 40,
|
||||||
maxHeight: 120,
|
maxHeight: 120,
|
||||||
resize: 'vertical',
|
resize: 'vertical',
|
||||||
padding: '10px 14px',
|
padding: '10px 14px',
|
||||||
|
|
@ -429,6 +439,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontFamily: 'inherit',
|
fontFamily: 'inherit',
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
|
flexBasis: isMobile ? '100%' : undefined,
|
||||||
}}
|
}}
|
||||||
rows={1}
|
rows={1}
|
||||||
/>
|
/>
|
||||||
|
|
@ -438,7 +449,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
disabled={uploading || isProcessing}
|
disabled={uploading || isProcessing}
|
||||||
title="Datei anhängen"
|
title="Datei anhängen"
|
||||||
style={{
|
style={{
|
||||||
width: 40, height: 40, borderRadius: 8, border: '1px solid var(--border-color, #ddd)',
|
width: _controlSize, height: _controlSize, borderRadius: 8, border: '1px solid var(--border-color, #ddd)',
|
||||||
background: 'var(--secondary-bg, #f5f5f5)',
|
background: 'var(--secondary-bg, #f5f5f5)',
|
||||||
color: uploading ? '#1976d2' : '#666',
|
color: uploading ? '#1976d2' : '#666',
|
||||||
cursor: uploading || isProcessing ? 'not-allowed' : 'pointer',
|
cursor: uploading || isProcessing ? 'not-allowed' : 'pointer',
|
||||||
|
|
@ -456,7 +467,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
disabled={isProcessing}
|
disabled={isProcessing}
|
||||||
title="Datenquellen anhängen"
|
title="Datenquellen anhängen"
|
||||||
style={{
|
style={{
|
||||||
width: 40, height: 40, borderRadius: 8, border: '1px solid var(--border-color, #ddd)',
|
width: _controlSize, height: _controlSize, borderRadius: 8, border: '1px solid var(--border-color, #ddd)',
|
||||||
background: attachedDataSourceIds.length > 0 ? '#e8f5e9' : 'var(--secondary-bg, #f5f5f5)',
|
background: attachedDataSourceIds.length > 0 ? '#e8f5e9' : 'var(--secondary-bg, #f5f5f5)',
|
||||||
color: attachedDataSourceIds.length > 0 ? '#2e7d32' : '#666',
|
color: attachedDataSourceIds.length > 0 ? '#2e7d32' : '#666',
|
||||||
cursor: isProcessing ? 'not-allowed' : 'pointer',
|
cursor: isProcessing ? 'not-allowed' : 'pointer',
|
||||||
|
|
@ -536,7 +547,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
onClick={() => setShowLangPicker(prev => !prev)}
|
onClick={() => setShowLangPicker(prev => !prev)}
|
||||||
title="Sprache waehlen"
|
title="Sprache waehlen"
|
||||||
style={{
|
style={{
|
||||||
height: 40, borderRadius: '8px 0 0 8px', border: '1px solid var(--border-color, #ddd)',
|
height: _controlSize, borderRadius: '8px 0 0 8px', border: '1px solid var(--border-color, #ddd)',
|
||||||
borderRight: 'none',
|
borderRight: 'none',
|
||||||
background: 'var(--secondary-bg, #f5f5f5)',
|
background: 'var(--secondary-bg, #f5f5f5)',
|
||||||
color: '#666', cursor: 'pointer', fontSize: 10, padding: '0 6px',
|
color: '#666', cursor: 'pointer', fontSize: 10, padding: '0 6px',
|
||||||
|
|
@ -549,7 +560,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
onClick={_toggleVoice}
|
onClick={_toggleVoice}
|
||||||
title={voiceActive ? 'Aufnahme stoppen' : 'Sprachaufnahme starten'}
|
title={voiceActive ? 'Aufnahme stoppen' : 'Sprachaufnahme starten'}
|
||||||
style={{
|
style={{
|
||||||
width: 40, height: 40, borderRadius: '0 8px 8px 0', border: 'none',
|
width: _controlSize, height: _controlSize, borderRadius: '0 8px 8px 0', border: 'none',
|
||||||
background: voiceActive ? '#f44336' : 'var(--secondary-bg, #f5f5f5)',
|
background: voiceActive ? '#f44336' : 'var(--secondary-bg, #f5f5f5)',
|
||||||
color: voiceActive ? '#fff' : '#666',
|
color: voiceActive ? '#fff' : '#666',
|
||||||
cursor: 'pointer', fontSize: 18, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
cursor: 'pointer', fontSize: 18, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
|
@ -589,6 +600,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
style={{
|
style={{
|
||||||
padding: '10px 20px', borderRadius: 8, border: 'none',
|
padding: '10px 20px', borderRadius: 8, border: 'none',
|
||||||
background: '#f44336', color: '#fff', cursor: 'pointer', fontWeight: 600,
|
background: '#f44336', color: '#fff', cursor: 'pointer', fontWeight: 600,
|
||||||
|
minWidth: isMobile ? 84 : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Stop
|
Stop
|
||||||
|
|
@ -601,6 +613,7 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({
|
||||||
padding: '10px 20px', borderRadius: 8, border: 'none',
|
padding: '10px 20px', borderRadius: 8, border: 'none',
|
||||||
background: prompt.trim() ? 'var(--primary-color, #1976d2)' : '#ccc',
|
background: prompt.trim() ? 'var(--primary-color, #1976d2)' : '#ccc',
|
||||||
color: '#fff', cursor: prompt.trim() ? 'pointer' : 'default', fontWeight: 600,
|
color: '#fff', cursor: prompt.trim() ? 'pointer' : 'default', fontWeight: 600,
|
||||||
|
minWidth: isMobile ? 84 : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Send
|
Send
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
* Right sidebar: FilePreview, ToolActivityLog
|
* Right sidebar: FilePreview, ToolActivityLog
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useCallback, useRef } from 'react';
|
import React, { useState, useCallback, useRef, useEffect } from 'react';
|
||||||
import { useCurrentInstance } from '../../../hooks/useCurrentInstance';
|
import { useCurrentInstance } from '../../../hooks/useCurrentInstance';
|
||||||
import { useFileOperations } from '../../../hooks/useFiles';
|
import { useFileOperations } from '../../../hooks/useFiles';
|
||||||
import { useWorkspace } from './useWorkspace';
|
import { useWorkspace } from './useWorkspace';
|
||||||
|
|
@ -80,6 +80,27 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
||||||
const [isDragOver, setIsDragOver] = useState(false);
|
const [isDragOver, setIsDragOver] = useState(false);
|
||||||
const dragCounterRef = useRef(0);
|
const dragCounterRef = useRef(0);
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const [isMobile, setIsMobile] = useState<boolean>(() =>
|
||||||
|
typeof window !== 'undefined' ? window.innerWidth <= 1024 : false,
|
||||||
|
);
|
||||||
|
const [mobileLeftOpen, setMobileLeftOpen] = useState(false);
|
||||||
|
const [mobileRightOpen, setMobileRightOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const _handleResize = () => {
|
||||||
|
setIsMobile(window.innerWidth <= 1024);
|
||||||
|
};
|
||||||
|
_handleResize();
|
||||||
|
window.addEventListener('resize', _handleResize);
|
||||||
|
return () => window.removeEventListener('resize', _handleResize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isMobile) {
|
||||||
|
setMobileLeftOpen(false);
|
||||||
|
setMobileRightOpen(false);
|
||||||
|
}
|
||||||
|
}, [isMobile]);
|
||||||
|
|
||||||
const _uploadAndAttach = useCallback(async (file: File) => {
|
const _uploadAndAttach = useCallback(async (file: File) => {
|
||||||
const result = await fileOps.handleFileUpload(file, undefined, instanceId);
|
const result = await fileOps.handleFileUpload(file, undefined, instanceId);
|
||||||
|
|
@ -147,6 +168,9 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
||||||
setSelectedFileId(fileId);
|
setSelectedFileId(fileId);
|
||||||
setRightTab('preview');
|
setRightTab('preview');
|
||||||
setRightCollapsed(false);
|
setRightCollapsed(false);
|
||||||
|
if (isMobile) {
|
||||||
|
setMobileRightOpen(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const _handleConversationSelect = (wfId: string) => {
|
const _handleConversationSelect = (wfId: string) => {
|
||||||
|
|
@ -166,10 +190,74 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
||||||
textTransform: 'uppercase' as const,
|
textTransform: 'uppercase' as const,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const _leftPanelBody = (
|
||||||
|
<>
|
||||||
|
<div style={{ display: 'flex', borderBottom: '1px solid var(--border-color, #e0e0e0)' }}>
|
||||||
|
<button style={tabButtonStyle(leftTab === 'conversations')} onClick={() => setLeftTab('conversations')}>Chats</button>
|
||||||
|
<button style={tabButtonStyle(leftTab === 'files')} onClick={() => setLeftTab('files')}>Files</button>
|
||||||
|
<button style={tabButtonStyle(leftTab === 'datasources')} onClick={() => setLeftTab('datasources')}>Sources</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ flex: 1, overflow: 'auto' }}>
|
||||||
|
{leftTab === 'conversations' && (
|
||||||
|
<ConversationList
|
||||||
|
instanceId={instanceId}
|
||||||
|
activeWorkflowId={workspace.workflowId}
|
||||||
|
onSelect={_handleConversationSelect}
|
||||||
|
onCreateNew={workspace.resetToNew}
|
||||||
|
refreshTrigger={workspace.workflowVersion}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{leftTab === 'files' && (
|
||||||
|
<FileBrowser
|
||||||
|
instanceId={instanceId}
|
||||||
|
files={workspace.files}
|
||||||
|
folders={workspace.folders}
|
||||||
|
onRefresh={workspace.refreshFiles}
|
||||||
|
onFileSelect={_handleFileSelect}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{leftTab === 'datasources' && (
|
||||||
|
<DataSourcePanel
|
||||||
|
instanceId={instanceId}
|
||||||
|
dataSources={workspace.dataSources}
|
||||||
|
onRefresh={workspace.refreshDataSources}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const _rightPanelBody = (
|
||||||
|
<>
|
||||||
|
<div style={{ padding: '6px 12px', borderBottom: '1px solid var(--border-color, #e0e0e0)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<div style={{ display: 'flex', gap: 8 }}>
|
||||||
|
<button style={tabButtonStyle(rightTab === 'activity')} onClick={() => setRightTab('activity')}>Activity</button>
|
||||||
|
<button style={tabButtonStyle(rightTab === 'preview')} onClick={() => setRightTab('preview')}>Preview</button>
|
||||||
|
</div>
|
||||||
|
{!isMobile && (
|
||||||
|
<button onClick={() => setRightCollapsed(true)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 14, color: '#888' }}>▶</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1, overflow: 'auto' }}>
|
||||||
|
{rightTab === 'activity' && (
|
||||||
|
<ToolActivityLog activities={workspace.toolActivities} />
|
||||||
|
)}
|
||||||
|
{rightTab === 'preview' && (
|
||||||
|
<FilePreview
|
||||||
|
instanceId={instanceId}
|
||||||
|
fileId={selectedFileId}
|
||||||
|
files={workspace.files}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden' }}>
|
<div style={{ display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', position: 'relative' }}>
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
{!leftCollapsed && (
|
{!isMobile && !leftCollapsed && (
|
||||||
<aside style={{
|
<aside style={{
|
||||||
width: _leftResize.width,
|
width: _leftResize.width,
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
|
|
@ -183,45 +271,12 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
||||||
<span style={{ fontWeight: 600, fontSize: 14 }}>Workspace</span>
|
<span style={{ fontWeight: 600, fontSize: 14 }}>Workspace</span>
|
||||||
<button onClick={() => setLeftCollapsed(true)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 14, color: '#888' }}>◀</button>
|
<button onClick={() => setLeftCollapsed(true)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 14, color: '#888' }}>◀</button>
|
||||||
</div>
|
</div>
|
||||||
|
{_leftPanelBody}
|
||||||
<div style={{ display: 'flex', borderBottom: '1px solid var(--border-color, #e0e0e0)' }}>
|
|
||||||
<button style={tabButtonStyle(leftTab === 'conversations')} onClick={() => setLeftTab('conversations')}>Chats</button>
|
|
||||||
<button style={tabButtonStyle(leftTab === 'files')} onClick={() => setLeftTab('files')}>Files</button>
|
|
||||||
<button style={tabButtonStyle(leftTab === 'datasources')} onClick={() => setLeftTab('datasources')}>Sources</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ flex: 1, overflow: 'auto' }}>
|
|
||||||
{leftTab === 'conversations' && (
|
|
||||||
<ConversationList
|
|
||||||
instanceId={instanceId}
|
|
||||||
activeWorkflowId={workspace.workflowId}
|
|
||||||
onSelect={_handleConversationSelect}
|
|
||||||
onCreateNew={workspace.resetToNew}
|
|
||||||
refreshTrigger={workspace.workflowVersion}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{leftTab === 'files' && (
|
|
||||||
<FileBrowser
|
|
||||||
instanceId={instanceId}
|
|
||||||
files={workspace.files}
|
|
||||||
folders={workspace.folders}
|
|
||||||
onRefresh={workspace.refreshFiles}
|
|
||||||
onFileSelect={_handleFileSelect}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{leftTab === 'datasources' && (
|
|
||||||
<DataSourcePanel
|
|
||||||
instanceId={instanceId}
|
|
||||||
dataSources={workspace.dataSources}
|
|
||||||
onRefresh={workspace.refreshDataSources}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</aside>
|
</aside>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Left resize handle */}
|
{/* Left resize handle */}
|
||||||
{!leftCollapsed && (
|
{!isMobile && !leftCollapsed && (
|
||||||
<div
|
<div
|
||||||
onMouseDown={e => _leftResize.onMouseDown(e, 1)}
|
onMouseDown={e => _leftResize.onMouseDown(e, 1)}
|
||||||
style={{ width: 4, cursor: 'col-resize', background: 'transparent', flexShrink: 0 }}
|
style={{ width: 4, cursor: 'col-resize', background: 'transparent', flexShrink: 0 }}
|
||||||
|
|
@ -230,7 +285,7 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{leftCollapsed && (
|
{!isMobile && leftCollapsed && (
|
||||||
<div style={{ width: 32, display: 'flex', alignItems: 'start', justifyContent: 'center', paddingTop: 8, borderRight: '1px solid var(--border-color, #e0e0e0)' }}>
|
<div style={{ width: 32, display: 'flex', alignItems: 'start', justifyContent: 'center', paddingTop: 8, borderRight: '1px solid var(--border-color, #e0e0e0)' }}>
|
||||||
<button onClick={() => setLeftCollapsed(false)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 14, color: '#888' }}>▶</button>
|
<button onClick={() => setLeftCollapsed(false)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 14, color: '#888' }}>▶</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -247,6 +302,57 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
||||||
onDragOver={_handleDragOver}
|
onDragOver={_handleDragOver}
|
||||||
onDrop={_handleDrop}
|
onDrop={_handleDrop}
|
||||||
>
|
>
|
||||||
|
{isMobile && (
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 8,
|
||||||
|
padding: '8px 12px',
|
||||||
|
borderBottom: '1px solid var(--border-color, #e0e0e0)',
|
||||||
|
background: 'var(--bg-primary, #fff)',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}}>
|
||||||
|
<button
|
||||||
|
onClick={() => setMobileLeftOpen(true)}
|
||||||
|
style={{
|
||||||
|
padding: '6px 10px',
|
||||||
|
borderRadius: 8,
|
||||||
|
border: '1px solid var(--border-color, #ddd)',
|
||||||
|
background: '#f7f7f7',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Workspace
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => { setRightTab('activity'); setMobileRightOpen(true); }}
|
||||||
|
style={{
|
||||||
|
padding: '6px 10px',
|
||||||
|
borderRadius: 8,
|
||||||
|
border: '1px solid var(--border-color, #ddd)',
|
||||||
|
background: rightTab === 'activity' ? '#e8f3ff' : '#f7f7f7',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Activity
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => { setRightTab('preview'); setMobileRightOpen(true); }}
|
||||||
|
style={{
|
||||||
|
padding: '6px 10px',
|
||||||
|
borderRadius: 8,
|
||||||
|
border: '1px solid var(--border-color, #ddd)',
|
||||||
|
background: rightTab === 'preview' ? '#e8f3ff' : '#f7f7f7',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Preview
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{isDragOver && (
|
{isDragOver && (
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute', inset: 0, zIndex: 100,
|
position: 'absolute', inset: 0, zIndex: 100,
|
||||||
|
|
@ -284,11 +390,12 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
||||||
uploading={fileOps.uploadingFile}
|
uploading={fileOps.uploadingFile}
|
||||||
selectedProviders={selectedProviders}
|
selectedProviders={selectedProviders}
|
||||||
onProvidersChange={setSelectedProviders}
|
onProvidersChange={setSelectedProviders}
|
||||||
|
isMobile={isMobile}
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{/* Right resize handle */}
|
{/* Right resize handle */}
|
||||||
{!rightCollapsed && (
|
{!isMobile && !rightCollapsed && (
|
||||||
<div
|
<div
|
||||||
onMouseDown={e => _rightResize.onMouseDown(e, -1)}
|
onMouseDown={e => _rightResize.onMouseDown(e, -1)}
|
||||||
style={{ width: 4, cursor: 'col-resize', background: 'transparent', flexShrink: 0 }}
|
style={{ width: 4, cursor: 'col-resize', background: 'transparent', flexShrink: 0 }}
|
||||||
|
|
@ -298,7 +405,7 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Right sidebar */}
|
{/* Right sidebar */}
|
||||||
{!rightCollapsed && (
|
{!isMobile && !rightCollapsed && (
|
||||||
<aside style={{
|
<aside style={{
|
||||||
width: _rightResize.width,
|
width: _rightResize.width,
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
|
|
@ -308,33 +415,82 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
}}>
|
}}>
|
||||||
<div style={{ padding: '6px 12px', borderBottom: '1px solid var(--border-color, #e0e0e0)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
{_rightPanelBody}
|
||||||
<div style={{ display: 'flex', gap: 8 }}>
|
|
||||||
<button style={tabButtonStyle(rightTab === 'activity')} onClick={() => setRightTab('activity')}>Activity</button>
|
|
||||||
<button style={tabButtonStyle(rightTab === 'preview')} onClick={() => setRightTab('preview')}>Preview</button>
|
|
||||||
</div>
|
|
||||||
<button onClick={() => setRightCollapsed(true)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 14, color: '#888' }}>▶</button>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, overflow: 'auto' }}>
|
|
||||||
{rightTab === 'activity' && (
|
|
||||||
<ToolActivityLog activities={workspace.toolActivities} />
|
|
||||||
)}
|
|
||||||
{rightTab === 'preview' && (
|
|
||||||
<FilePreview
|
|
||||||
instanceId={instanceId}
|
|
||||||
fileId={selectedFileId}
|
|
||||||
files={workspace.files}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</aside>
|
</aside>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{rightCollapsed && (
|
{!isMobile && rightCollapsed && (
|
||||||
<div style={{ width: 32, display: 'flex', alignItems: 'start', justifyContent: 'center', paddingTop: 8, borderLeft: '1px solid var(--border-color, #e0e0e0)' }}>
|
<div style={{ width: 32, display: 'flex', alignItems: 'start', justifyContent: 'center', paddingTop: 8, borderLeft: '1px solid var(--border-color, #e0e0e0)' }}>
|
||||||
<button onClick={() => setRightCollapsed(false)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 14, color: '#888' }}>◀</button>
|
<button onClick={() => setRightCollapsed(false)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 14, color: '#888' }}>◀</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isMobile && mobileLeftOpen && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
background: 'rgba(0, 0, 0, 0.35)',
|
||||||
|
zIndex: 120,
|
||||||
|
display: 'flex',
|
||||||
|
}}
|
||||||
|
onClick={() => setMobileLeftOpen(false)}
|
||||||
|
>
|
||||||
|
<aside
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 460,
|
||||||
|
height: '100%',
|
||||||
|
background: 'var(--bg-primary, #fff)',
|
||||||
|
borderRight: '1px solid var(--border-color, #e0e0e0)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div style={{ padding: '10px 12px', borderBottom: '1px solid var(--border-color, #e0e0e0)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<span style={{ fontWeight: 600, fontSize: 14 }}>Workspace</span>
|
||||||
|
<button onClick={() => setMobileLeftOpen(false)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 18, color: '#666' }}>×</button>
|
||||||
|
</div>
|
||||||
|
{_leftPanelBody}
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isMobile && mobileRightOpen && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
background: 'rgba(0, 0, 0, 0.35)',
|
||||||
|
zIndex: 120,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
}}
|
||||||
|
onClick={() => setMobileRightOpen(false)}
|
||||||
|
>
|
||||||
|
<aside
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 460,
|
||||||
|
height: '100%',
|
||||||
|
background: 'var(--bg-primary, #fff)',
|
||||||
|
borderLeft: '1px solid var(--border-color, #e0e0e0)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div style={{ padding: '10px 12px', borderBottom: '1px solid var(--border-color, #e0e0e0)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<span style={{ fontWeight: 600, fontSize: 14 }}>{rightTab === 'activity' ? 'Activity' : 'Preview'}</span>
|
||||||
|
<button onClick={() => setMobileRightOpen(false)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 18, color: '#666' }}>×</button>
|
||||||
|
</div>
|
||||||
|
{_rightPanelBody}
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue