Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Commit
·
a3d40b4
1
Parent(s):
4b590f9
rename aitube2 to tikslop
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- DEPLOYMENT.md +13 -13
- README.md +10 -10
- android/app/build.gradle +2 -2
- android/app/src/main/AndroidManifest.xml +1 -1
- android/app/src/main/kotlin/com/example/{aitube2 → tikslop}/MainActivity.kt +1 -1
- api_config.py +2 -2
- assets/logo/{aitube.png → tikslop.png} +0 -0
- assets/logo/{aitube.svg → tikslop.svg} +0 -0
- build/web/assets/AssetManifest.bin +1 -1
- build/web/assets/AssetManifest.bin.json +1 -1
- build/web/assets/AssetManifest.json +1 -1
- build/web/assets/assets/config/aitube.yaml +0 -61
- assets/config/aitube.yaml → build/web/assets/assets/config/tikslop.yaml +0 -0
- build/web/flutter_bootstrap.js +1 -1
- build/web/flutter_service_worker.js +13 -13
- build/web/index.html +4 -4
- build/web/main.dart.js +0 -0
- build/web/manifest.json +2 -2
- build/web/{aitube.png → tikslop.png} +0 -0
- build/web/{aitube.svg → tikslop.svg} +0 -0
- build/web/version.json +1 -1
- ios/Runner.xcodeproj/project.pbxproj +6 -6
- ios/Runner/Info.plist +2 -2
- lib/config/README.md +2 -2
- lib/config/config.dart +1 -1
- lib/config/secrets.sample.dart +1 -1
- lib/main.dart +36 -36
- lib/screens/home_screen.dart +26 -26
- lib/screens/settings_screen.dart +4 -4
- lib/screens/video_screen.dart +28 -28
- lib/services/chat_service.dart +1 -1
- lib/services/clip_queue/clip_generation_handler.dart +1 -1
- lib/services/clip_queue/clip_queue_manager.dart +1 -1
- lib/services/settings_service.dart +1 -1
- lib/services/websocket_api_service.dart +8 -8
- lib/theme/colors.dart +1 -1
- lib/widgets/api_key_dialog.dart +6 -6
- lib/widgets/chat_widget.dart +16 -16
- lib/widgets/maintenance_screen.dart +2 -2
- lib/widgets/search_box.dart +4 -4
- lib/widgets/video_card.dart +12 -12
- lib/widgets/video_player/buffer_manager.dart +5 -5
- lib/widgets/video_player/nano_clip_manager.dart +5 -5
- lib/widgets/video_player/nano_video_player.dart +6 -6
- lib/widgets/video_player/playback_controller.dart +2 -2
- lib/widgets/video_player/ui_components.dart +4 -4
- lib/widgets/video_player/video_player_widget.dart +5 -5
- lib/widgets/web_utils.dart +1 -1
- linux/CMakeLists.txt +2 -2
- linux/my_application.cc +2 -2
DEPLOYMENT.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
|
2 |
-
## Deploying
|
3 |
|
4 |
-
Note: this document is meant for
|
5 |
|
6 |
Aitube is not an app/tool but a website, it is not designed to be cloned (technically you can, but since this is not a goal it is not documented).
|
7 |
|
@@ -29,7 +29,7 @@ See paragraph "Running the gateway scheduler"
|
|
29 |
|
30 |
### Deployment to production
|
31 |
|
32 |
-
To deploy the
|
33 |
|
34 |
$ flutter build web
|
35 |
$ git add .
|
@@ -38,13 +38,13 @@ To deploy the aitube2 api to production:
|
|
38 |
|
39 |
and upload the assets to:
|
40 |
|
41 |
-
https://huggingface.co/spaces/jbilcke-hf/
|
42 |
|
43 |
#### Running a rendering node
|
44 |
|
45 |
-
Current
|
46 |
|
47 |
-
|
48 |
This helps ensuring a smooth attribution of requests.
|
49 |
|
50 |
What works well is to use the target number of users in parallel (eg. 3) and use 50% more capability to make sure we can handle the load, so in this case about 5 servers.
|
@@ -62,7 +62,7 @@ curl https://api.endpoints.huggingface.cloud/v2/endpoint/<YOUR_ACCOUNT_NAME> -X
|
|
62 |
# (if you haven't done it already for this shell session)
|
63 |
source .python_venv/bin/activate
|
64 |
|
65 |
-
PRODUCT_NAME="
|
66 |
MAX_NODES="3" \
|
67 |
MAINTENANCE_MODE=false \
|
68 |
HF_TOKEN="<USE YOUR OWN HF TOKEN>" \
|
@@ -82,10 +82,10 @@ PRODUCT_NAME="AiTube" \
|
|
82 |
|
83 |
```bash
|
84 |
# For local development with default configuration
|
85 |
-
flutter run --dart-define=CONFIG_PATH=assets/config/
|
86 |
|
87 |
# For production build to be deployed on a server
|
88 |
-
flutter build web --dart-define=CONFIG_PATH=assets/config/
|
89 |
```
|
90 |
|
91 |
### WebSocket Connection
|
@@ -98,22 +98,22 @@ The application automatically determines the WebSocket URL:
|
|
98 |
- No configuration needed for deployment
|
99 |
|
100 |
2. **Native Platforms**:
|
101 |
-
- Production: Automatically uses `wss://
|
102 |
- Development: Uses `ws://localhost:8080/ws` by default
|
103 |
- Custom: Can override with `API_WS_URL` environment variable (highest priority)
|
104 |
|
105 |
#### Production Native Build
|
106 |
|
107 |
-
For production builds (connecting to
|
108 |
```bash
|
109 |
-
flutter build apk --dart-define=CONFIG_PATH=assets/config/
|
110 |
```
|
111 |
|
112 |
#### Custom API Server
|
113 |
|
114 |
For connecting to a different server:
|
115 |
```bash
|
116 |
-
flutter build apk --dart-define=CONFIG_PATH=assets/config/
|
117 |
```
|
118 |
|
119 |
Note: The `API_WS_URL` parameter takes precedence over the production setting.
|
|
|
1 |
|
2 |
+
## Deploying tikslop to https://tikslop.at
|
3 |
|
4 |
+
Note: this document is meant for tikslop administrators only, not the general public.
|
5 |
|
6 |
Aitube is not an app/tool but a website, it is not designed to be cloned (technically you can, but since this is not a goal it is not documented).
|
7 |
|
|
|
29 |
|
30 |
### Deployment to production
|
31 |
|
32 |
+
To deploy the tikslop api to production:
|
33 |
|
34 |
$ flutter build web
|
35 |
$ git add .
|
|
|
38 |
|
39 |
and upload the assets to:
|
40 |
|
41 |
+
https://huggingface.co/spaces/jbilcke-hf/tikslop/tree/main/public
|
42 |
|
43 |
#### Running a rendering node
|
44 |
|
45 |
+
Current tikslop uses `jbilcke-hf/LTX-Video-0-9-6-HFIE` as a rendering node.
|
46 |
|
47 |
+
tikslop uses a round-robin schedule implemented on the gateway.
|
48 |
This helps ensuring a smooth attribution of requests.
|
49 |
|
50 |
What works well is to use the target number of users in parallel (eg. 3) and use 50% more capability to make sure we can handle the load, so in this case about 5 servers.
|
|
|
62 |
# (if you haven't done it already for this shell session)
|
63 |
source .python_venv/bin/activate
|
64 |
|
65 |
+
PRODUCT_NAME="TikSlop" \
|
66 |
MAX_NODES="3" \
|
67 |
MAINTENANCE_MODE=false \
|
68 |
HF_TOKEN="<USE YOUR OWN HF TOKEN>" \
|
|
|
82 |
|
83 |
```bash
|
84 |
# For local development with default configuration
|
85 |
+
flutter run --dart-define=CONFIG_PATH=assets/config/tikslop.yaml -d chrome
|
86 |
|
87 |
# For production build to be deployed on a server
|
88 |
+
flutter build web --dart-define=CONFIG_PATH=assets/config/tikslop.yaml
|
89 |
```
|
90 |
|
91 |
### WebSocket Connection
|
|
|
98 |
- No configuration needed for deployment
|
99 |
|
100 |
2. **Native Platforms**:
|
101 |
+
- Production: Automatically uses `wss://tikslop.at/ws` when built with production flag
|
102 |
- Development: Uses `ws://localhost:8080/ws` by default
|
103 |
- Custom: Can override with `API_WS_URL` environment variable (highest priority)
|
104 |
|
105 |
#### Production Native Build
|
106 |
|
107 |
+
For production builds (connecting to tikslop.at):
|
108 |
```bash
|
109 |
+
flutter build apk --dart-define=CONFIG_PATH=assets/config/tikslop.yaml --dart-define=PRODUCTION_MODE=true
|
110 |
```
|
111 |
|
112 |
#### Custom API Server
|
113 |
|
114 |
For connecting to a different server:
|
115 |
```bash
|
116 |
+
flutter build apk --dart-define=CONFIG_PATH=assets/config/tikslop.yaml --dart-define=API_WS_URL=ws://custom-api.example.com/ws
|
117 |
```
|
118 |
|
119 |
Note: The `API_WS_URL` parameter takes precedence over the production setting.
|
README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
---
|
2 |
-
title: "#
|
3 |
emoji: 🍿
|
4 |
colorFrom: red
|
5 |
colorTo: red
|
@@ -19,41 +19,41 @@ hf_oauth_scopes:
|
|
19 |
---
|
20 |
|
21 |
|
22 |
-
# #
|
23 |
|
24 |
## Configuration
|
25 |
|
26 |
### WebSocket Connection
|
27 |
- **Web Platform**: Automatically connects to the host serving the page (adapts to both HTTP/HTTPS)
|
28 |
- **Native Platforms**:
|
29 |
-
- Production: Uses `wss://
|
30 |
- Development: Uses `ws://localhost:8080/ws` by default
|
31 |
- Custom: Set `API_WS_URL` during build with `--dart-define=API_WS_URL=ws://your-server:port/ws` (highest priority)
|
32 |
|
33 |
## News
|
34 |
|
35 |
-
#
|
36 |
|
37 |
Stay hooked at @flngr on X!
|
38 |
|
39 |
|
40 |
-
## What is
|
41 |
|
42 |
-
#
|
43 |
|
44 |
-
In [#
|
45 |
|
46 |
This allows for new ways of consuming AI generated content, such as collaborative and interactive prompting.
|
47 |
|
48 |
# Where can I use it?
|
49 |
|
50 |
-
#
|
51 |
|
52 |
# Why can't I use it?
|
53 |
|
54 |
As this is a personal project I only have limited ressources to develop it on the side, but there are also technological bottlenecks.
|
55 |
|
56 |
-
Right now it is not economically viable to operate a platform like
|
57 |
|
58 |
I am evaluating various options to make it available sooner for people with the financial ressources to try it, such as creating a system to deploy render nodes to Hugging Face, GPU-on-demand blockchains.. etc.
|
59 |
|
@@ -61,6 +61,6 @@ I am evaluating various options to make it available sooner for people with the
|
|
61 |
|
62 |
I estimate it will take up to 1 to 2 years for more powerful and/or cheaper hardware to become available.
|
63 |
|
64 |
-
I already have a open-source prototype of
|
65 |
|
66 |
It's not representative of the final experience, but that's a start and I use that as a basis to imagine the experiences of the future (collaborative generation, broadcasted live streams, interactive gaming, and artistic experiences that are hybrid between video and gaming).
|
|
|
1 |
---
|
2 |
+
title: "#tikslop"
|
3 |
emoji: 🍿
|
4 |
colorFrom: red
|
5 |
colorTo: red
|
|
|
19 |
---
|
20 |
|
21 |
|
22 |
+
# #tikslop
|
23 |
|
24 |
## Configuration
|
25 |
|
26 |
### WebSocket Connection
|
27 |
- **Web Platform**: Automatically connects to the host serving the page (adapts to both HTTP/HTTPS)
|
28 |
- **Native Platforms**:
|
29 |
+
- Production: Uses `wss://tikslop.at/ws` when built with `--dart-define=PRODUCTION_MODE=true`
|
30 |
- Development: Uses `ws://localhost:8080/ws` by default
|
31 |
- Custom: Set `API_WS_URL` during build with `--dart-define=API_WS_URL=ws://your-server:port/ws` (highest priority)
|
32 |
|
33 |
## News
|
34 |
|
35 |
+
#tikslop is coming sooner than expected!
|
36 |
|
37 |
Stay hooked at @flngr on X!
|
38 |
|
39 |
|
40 |
+
## What is TikSlop?
|
41 |
|
42 |
+
#tikslop is a reboot of [TikSlop 1](https://x.com/danielpikl/status/1737882643625078835), a project made in 2023 which generated AI videos in the background using LLM agents, to simulate an AI generated video platform.
|
43 |
|
44 |
+
In [#tikslop](https://x.com/flngr/status/1864127796945011016), this concept is put upside down: now the content is generated on demand (when the user types something in the latent search input) and on the fly (video chunks are generated within a few seconds and streamed continuously).
|
45 |
|
46 |
This allows for new ways of consuming AI generated content, such as collaborative and interactive prompting.
|
47 |
|
48 |
# Where can I use it?
|
49 |
|
50 |
+
#tikslop is not ready yet: this is an experimental side project and the [platform](https://tikslop.at), code and documentation will be in development for most of 2025.
|
51 |
|
52 |
# Why can't I use it?
|
53 |
|
54 |
As this is a personal project I only have limited ressources to develop it on the side, but there are also technological bottlenecks.
|
55 |
|
56 |
+
Right now it is not economically viable to operate a platform like TikSlop, it requires hardware that is too expensive and/or not powerful enough to give an enjoyable and reactive streaming experience.
|
57 |
|
58 |
I am evaluating various options to make it available sooner for people with the financial ressources to try it, such as creating a system to deploy render nodes to Hugging Face, GPU-on-demand blockchains.. etc.
|
59 |
|
|
|
61 |
|
62 |
I estimate it will take up to 1 to 2 years for more powerful and/or cheaper hardware to become available.
|
63 |
|
64 |
+
I already have a open-source prototype of TikSlop which I use for R&D, based on free (as in "can run on your own machine") AI video models that can run fast with low quality settings (such as LTX Video).
|
65 |
|
66 |
It's not representative of the final experience, but that's a start and I use that as a basis to imagine the experiences of the future (collaborative generation, broadcasted live streams, interactive gaming, and artistic experiences that are hybrid between video and gaming).
|
android/app/build.gradle
CHANGED
@@ -6,7 +6,7 @@ plugins {
|
|
6 |
}
|
7 |
|
8 |
android {
|
9 |
-
namespace = "com.example.
|
10 |
compileSdk = flutter.compileSdkVersion
|
11 |
ndkVersion = flutter.ndkVersion
|
12 |
|
@@ -21,7 +21,7 @@ android {
|
|
21 |
|
22 |
defaultConfig {
|
23 |
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
24 |
-
applicationId = "com.example.
|
25 |
// You can update the following values to match your application needs.
|
26 |
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
27 |
minSdk = flutter.minSdkVersion
|
|
|
6 |
}
|
7 |
|
8 |
android {
|
9 |
+
namespace = "com.example.tikslop"
|
10 |
compileSdk = flutter.compileSdkVersion
|
11 |
ndkVersion = flutter.ndkVersion
|
12 |
|
|
|
21 |
|
22 |
defaultConfig {
|
23 |
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
24 |
+
applicationId = "com.example.tikslop"
|
25 |
// You can update the following values to match your application needs.
|
26 |
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
27 |
minSdk = flutter.minSdkVersion
|
android/app/src/main/AndroidManifest.xml
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
2 |
<application
|
3 |
-
android:label="
|
4 |
android:name="${applicationName}"
|
5 |
android:icon="@mipmap/ic_launcher">
|
6 |
<activity
|
|
|
1 |
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
2 |
<application
|
3 |
+
android:label="tikslop"
|
4 |
android:name="${applicationName}"
|
5 |
android:icon="@mipmap/ic_launcher">
|
6 |
<activity
|
android/app/src/main/kotlin/com/example/{aitube2 → tikslop}/MainActivity.kt
RENAMED
@@ -1,4 +1,4 @@
|
|
1 |
-
package com.example.
|
2 |
|
3 |
import io.flutter.embedding.android.FlutterActivity
|
4 |
|
|
|
1 |
+
package com.example.tikslop
|
2 |
|
3 |
import io.flutter.embedding.android.FlutterActivity
|
4 |
|
api_config.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import os
|
2 |
|
3 |
-
PRODUCT_NAME = os.environ.get('PRODUCT_NAME', '
|
4 |
PRODUCT_VERSION = "2.0.0"
|
5 |
|
6 |
# you should use Mistral 7b instruct for good performance and accuracy balance
|
@@ -47,7 +47,7 @@ GUIDANCE_SCALE = 1.0
|
|
47 |
|
48 |
THUMBNAIL_FRAMES = 65
|
49 |
|
50 |
-
# anonymous users are people browing
|
51 |
# this category suffers from regular abuse so we need to enforce strict limitations
|
52 |
CONFIG_FOR_ANONYMOUS_USERS = {
|
53 |
|
|
|
1 |
import os
|
2 |
|
3 |
+
PRODUCT_NAME = os.environ.get('PRODUCT_NAME', 'TikSlop')
|
4 |
PRODUCT_VERSION = "2.0.0"
|
5 |
|
6 |
# you should use Mistral 7b instruct for good performance and accuracy balance
|
|
|
47 |
|
48 |
THUMBNAIL_FRAMES = 65
|
49 |
|
50 |
+
# anonymous users are people browing TikSlop without being connected
|
51 |
# this category suffers from regular abuse so we need to enforce strict limitations
|
52 |
CONFIG_FOR_ANONYMOUS_USERS = {
|
53 |
|
assets/logo/{aitube.png → tikslop.png}
RENAMED
File without changes
|
assets/logo/{aitube.svg → tikslop.svg}
RENAMED
File without changes
|
build/web/assets/AssetManifest.bin
CHANGED
@@ -1 +1 @@
|
|
1 |
-
|
|
|
1 |
+
|
build/web/assets/AssetManifest.bin.json
CHANGED
@@ -1 +1 @@
|
|
1 |
-
"
|
|
|
1 |
+
"DQUHF2Fzc2V0cy9jb25maWcvUkVBRE1FLm1kDAENAQcFYXNzZXQHF2Fzc2V0cy9jb25maWcvUkVBRE1FLm1kBxlhc3NldHMvY29uZmlnL2N1c3RvbS55YW1sDAENAQcFYXNzZXQHGWFzc2V0cy9jb25maWcvY3VzdG9tLnlhbWwHGmFzc2V0cy9jb25maWcvZGVmYXVsdC55YW1sDAENAQcFYXNzZXQHGmFzc2V0cy9jb25maWcvZGVmYXVsdC55YW1sBxphc3NldHMvY29uZmlnL3Rpa3Nsb3AueWFtbAwBDQEHBWFzc2V0Bxphc3NldHMvY29uZmlnL3Rpa3Nsb3AueWFtbAcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGYMAQ0BBwVhc3NldAcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGY="
|
build/web/assets/AssetManifest.json
CHANGED
@@ -1 +1 @@
|
|
1 |
-
{"assets/config/README.md":["assets/config/README.md"],"assets/config/
|
|
|
1 |
+
{"assets/config/README.md":["assets/config/README.md"],"assets/config/custom.yaml":["assets/config/custom.yaml"],"assets/config/default.yaml":["assets/config/default.yaml"],"assets/config/tikslop.yaml":["assets/config/tikslop.yaml"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"]}
|
build/web/assets/assets/config/aitube.yaml
DELETED
@@ -1,61 +0,0 @@
|
|
1 |
-
ui:
|
2 |
-
product_name: "#aitube2"
|
3 |
-
showChatInVideoView: false
|
4 |
-
|
5 |
-
render_queue:
|
6 |
-
# how many clips should be stored in advance
|
7 |
-
buffer_size: 3
|
8 |
-
|
9 |
-
# how many requests for clips can be run in parallel
|
10 |
-
max_concurrent_generations: 2
|
11 |
-
|
12 |
-
# start playback as soon as we have 1 video over 3 (25%)
|
13 |
-
minimum_buffer_percent_to_start_playback: 5
|
14 |
-
|
15 |
-
simulation:
|
16 |
-
# how often the description should evolve (in seconds)
|
17 |
-
# setting to 0 disables description evolution
|
18 |
-
sim_loop_frequency_in_sec: 10
|
19 |
-
|
20 |
-
# it's OK to use high values here,
|
21 |
-
# because some of those values are limited by the backend config,
|
22 |
-
# such as the resoltuion or number of frames
|
23 |
-
video:
|
24 |
-
default_negative_prompt: ""
|
25 |
-
|
26 |
-
# transition time between each clip
|
27 |
-
# the exit (older) clip will see its playback time reduced by this amount
|
28 |
-
transition_buffer_duration_ms: 300
|
29 |
-
|
30 |
-
# how long a generated clip should be, in Duration
|
31 |
-
original_clip_duration_seconds: 3
|
32 |
-
|
33 |
-
# The model works on resolutions that are divisible by 32
|
34 |
-
# and number of frames that are divisible by 8 + 1 (e.g. 257).
|
35 |
-
#
|
36 |
-
# In case the resolution or number of frames are not divisible
|
37 |
-
# by 32 or 8 + 1, the input will be padded with -1 and then
|
38 |
-
# cropped to the desired resolution and number of frames.
|
39 |
-
#
|
40 |
-
# The model works best on resolutions under 720 x 1280 and
|
41 |
-
# number of frames below 257.
|
42 |
-
|
43 |
-
# number of inference steps
|
44 |
-
# (this is capped by the backend API)
|
45 |
-
num_inference_steps: 8
|
46 |
-
|
47 |
-
guidance_scale: 1.0
|
48 |
-
|
49 |
-
# original frame-rate of each clip (before we slow them down)
|
50 |
-
# in frames per second (so an integer)
|
51 |
-
original_clip_frame_rate: 25
|
52 |
-
|
53 |
-
# (this is capped by the backend API)
|
54 |
-
original_clip_width: 1216
|
55 |
-
|
56 |
-
# (this is capped by the backend API)
|
57 |
-
original_clip_height: 672
|
58 |
-
|
59 |
-
# to do more with less, we slow down the videos (a 3s video will become a 4s video)
|
60 |
-
# but if you are GPU rich feel feel to play them back at 100% of their speed!
|
61 |
-
clip_playback_speed: 0.7
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/config/aitube.yaml → build/web/assets/assets/config/tikslop.yaml
RENAMED
File without changes
|
build/web/flutter_bootstrap.js
CHANGED
@@ -39,6 +39,6 @@ _flutter.buildConfig = {"engineRevision":"382be0028d370607f76215a9be322e5514b263
|
|
39 |
|
40 |
_flutter.loader.load({
|
41 |
serviceWorkerSettings: {
|
42 |
-
serviceWorkerVersion: "
|
43 |
}
|
44 |
});
|
|
|
39 |
|
40 |
_flutter.loader.load({
|
41 |
serviceWorkerSettings: {
|
42 |
+
serviceWorkerVersion: "3718327370"
|
43 |
}
|
44 |
});
|
build/web/flutter_service_worker.js
CHANGED
@@ -3,31 +3,32 @@ const MANIFEST = 'flutter-app-manifest';
|
|
3 |
const TEMP = 'flutter-temp-cache';
|
4 |
const CACHE_NAME = 'flutter-app-cache';
|
5 |
|
6 |
-
const RESOURCES = {"flutter_bootstrap.js": "
|
7 |
-
"version.json": "
|
8 |
-
"
|
9 |
-
"
|
10 |
-
"
|
|
|
|
|
11 |
"flutter.js": "83d881c1dbb6d6bcd6b42e274605b69c",
|
12 |
-
"aitube.svg": "26140ba0d153b213b122bc6ebcc17f6c",
|
13 |
"favicon.png": "c8a183c516004e648a7bac7497c89b97",
|
14 |
"icons/Icon-192.png": "9d17785814071b986002307441ec7a8f",
|
15 |
"icons/Icon-maskable-192.png": "9d17785814071b986002307441ec7a8f",
|
16 |
"icons/Icon-maskable-512.png": "8682b581a7dab984ef4f9b7f21976a64",
|
17 |
"icons/Icon-512.png": "8682b581a7dab984ef4f9b7f21976a64",
|
18 |
-
"manifest.json": "
|
19 |
-
"assets/AssetManifest.json": "
|
20 |
"assets/NOTICES": "f0cfae681e209e19b2b144a9f062a96f",
|
21 |
"assets/FontManifest.json": "dc3d03800ccca4601324923c0b1d6d57",
|
22 |
-
"assets/AssetManifest.bin.json": "
|
23 |
"assets/packages/cupertino_icons/assets/CupertinoIcons.ttf": "33b7d9392238c04c131b6ce224e13711",
|
24 |
"assets/shaders/ink_sparkle.frag": "ecc85a2e95f5e9f53123dcaf8cb9b6ce",
|
25 |
-
"assets/AssetManifest.bin": "
|
26 |
"assets/fonts/MaterialIcons-Regular.otf": "06b86454c633cc9510ad85ddc0523a91",
|
27 |
"assets/assets/config/README.md": "07a87720dd00dd1ca98c9d6884440e31",
|
28 |
"assets/assets/config/custom.yaml": "e5c0b238b6f217f1215fbc813f093656",
|
29 |
-
"assets/assets/config/aitube.yaml": "7df9de77da965c3bea94f2c6d33a4b5f",
|
30 |
"assets/assets/config/default.yaml": "d1304586fd15839a754f53dda3dd8a44",
|
|
|
31 |
"canvaskit/skwasm.js": "ea559890a088fe28b4ddf70e17e60052",
|
32 |
"canvaskit/skwasm.js.symbols": "9fe690d47b904d72c7d020bd303adf16",
|
33 |
"canvaskit/canvaskit.js.symbols": "27361387bc24144b46a745f1afe92b50",
|
@@ -36,8 +37,7 @@ const RESOURCES = {"flutter_bootstrap.js": "aa0de14d9015e57eee1c8b8d203d37cc",
|
|
36 |
"canvaskit/chromium/canvaskit.js": "8191e843020c832c9cf8852a4b909d4c",
|
37 |
"canvaskit/chromium/canvaskit.wasm": "c054c2c892172308ca5a0bd1d7a7754b",
|
38 |
"canvaskit/canvaskit.js": "728b2d477d9b8c14593d4f9b82b484f3",
|
39 |
-
"canvaskit/canvaskit.wasm": "a37f2b0af4995714de856e21e882325c"
|
40 |
-
"aitube.png": "570e1db759046e2d224fef729983634e"};
|
41 |
// The application shell files that are downloaded before a service worker can
|
42 |
// start.
|
43 |
const CORE = ["main.dart.js",
|
|
|
3 |
const TEMP = 'flutter-temp-cache';
|
4 |
const CACHE_NAME = 'flutter-app-cache';
|
5 |
|
6 |
+
const RESOURCES = {"flutter_bootstrap.js": "fe60adda51a3823633b786075b6d10eb",
|
7 |
+
"version.json": "68350cac7987de2728345c72918dd067",
|
8 |
+
"tikslop.png": "570e1db759046e2d224fef729983634e",
|
9 |
+
"index.html": "820eb02576e6d66488611f1b19e15b63",
|
10 |
+
"/": "820eb02576e6d66488611f1b19e15b63",
|
11 |
+
"main.dart.js": "2c8a8ae55422254bad36b497dd408761",
|
12 |
+
"tikslop.svg": "26140ba0d153b213b122bc6ebcc17f6c",
|
13 |
"flutter.js": "83d881c1dbb6d6bcd6b42e274605b69c",
|
|
|
14 |
"favicon.png": "c8a183c516004e648a7bac7497c89b97",
|
15 |
"icons/Icon-192.png": "9d17785814071b986002307441ec7a8f",
|
16 |
"icons/Icon-maskable-192.png": "9d17785814071b986002307441ec7a8f",
|
17 |
"icons/Icon-maskable-512.png": "8682b581a7dab984ef4f9b7f21976a64",
|
18 |
"icons/Icon-512.png": "8682b581a7dab984ef4f9b7f21976a64",
|
19 |
+
"manifest.json": "c0904388ddaba6a9bd572a80f79a8dcc",
|
20 |
+
"assets/AssetManifest.json": "bf40bd52b84d1c4e4f27946b18178ffc",
|
21 |
"assets/NOTICES": "f0cfae681e209e19b2b144a9f062a96f",
|
22 |
"assets/FontManifest.json": "dc3d03800ccca4601324923c0b1d6d57",
|
23 |
+
"assets/AssetManifest.bin.json": "f7cde9e4f9fb8303a6858e8ce1b573db",
|
24 |
"assets/packages/cupertino_icons/assets/CupertinoIcons.ttf": "33b7d9392238c04c131b6ce224e13711",
|
25 |
"assets/shaders/ink_sparkle.frag": "ecc85a2e95f5e9f53123dcaf8cb9b6ce",
|
26 |
+
"assets/AssetManifest.bin": "87dcd71e038a4b45c095cccdab2777d4",
|
27 |
"assets/fonts/MaterialIcons-Regular.otf": "06b86454c633cc9510ad85ddc0523a91",
|
28 |
"assets/assets/config/README.md": "07a87720dd00dd1ca98c9d6884440e31",
|
29 |
"assets/assets/config/custom.yaml": "e5c0b238b6f217f1215fbc813f093656",
|
|
|
30 |
"assets/assets/config/default.yaml": "d1304586fd15839a754f53dda3dd8a44",
|
31 |
+
"assets/assets/config/tikslop.yaml": "7df9de77da965c3bea94f2c6d33a4b5f",
|
32 |
"canvaskit/skwasm.js": "ea559890a088fe28b4ddf70e17e60052",
|
33 |
"canvaskit/skwasm.js.symbols": "9fe690d47b904d72c7d020bd303adf16",
|
34 |
"canvaskit/canvaskit.js.symbols": "27361387bc24144b46a745f1afe92b50",
|
|
|
37 |
"canvaskit/chromium/canvaskit.js": "8191e843020c832c9cf8852a4b909d4c",
|
38 |
"canvaskit/chromium/canvaskit.wasm": "c054c2c892172308ca5a0bd1d7a7754b",
|
39 |
"canvaskit/canvaskit.js": "728b2d477d9b8c14593d4f9b82b484f3",
|
40 |
+
"canvaskit/canvaskit.wasm": "a37f2b0af4995714de856e21e882325c"};
|
|
|
41 |
// The application shell files that are downloaded before a service worker can
|
42 |
// start.
|
43 |
const CORE = ["main.dart.js",
|
build/web/index.html
CHANGED
@@ -29,7 +29,7 @@
|
|
29 |
<!-- Favicon -->
|
30 |
<link rel="icon" type="image/png" href="favicon.png"/>
|
31 |
|
32 |
-
<title>#
|
33 |
<link rel="manifest" href="manifest.json">
|
34 |
|
35 |
<style>
|
@@ -130,9 +130,9 @@
|
|
130 |
|
131 |
<div class="loading-container" id="loading">
|
132 |
<div class="logo-container">
|
133 |
-
<img src="
|
134 |
</div>
|
135 |
-
<div class="loading-text">Loading #
|
136 |
</div>
|
137 |
|
138 |
<script>
|
@@ -156,7 +156,7 @@
|
|
156 |
</script>
|
157 |
|
158 |
<!-- Add version parameter for cache busting -->
|
159 |
-
<script src="flutter_bootstrap.js?v=
|
160 |
|
161 |
<!-- Add cache busting script -->
|
162 |
<script>
|
|
|
29 |
<!-- Favicon -->
|
30 |
<link rel="icon" type="image/png" href="favicon.png"/>
|
31 |
|
32 |
+
<title>#tikslop</title>
|
33 |
<link rel="manifest" href="manifest.json">
|
34 |
|
35 |
<style>
|
|
|
130 |
|
131 |
<div class="loading-container" id="loading">
|
132 |
<div class="logo-container">
|
133 |
+
<img src="tikslop.svg" alt="AITube Logo" class="logo">
|
134 |
</div>
|
135 |
+
<div class="loading-text">Loading #tikslop...</div>
|
136 |
</div>
|
137 |
|
138 |
<script>
|
|
|
156 |
</script>
|
157 |
|
158 |
<!-- Add version parameter for cache busting -->
|
159 |
+
<script src="flutter_bootstrap.js?v=1747138236" async></script>
|
160 |
|
161 |
<!-- Add cache busting script -->
|
162 |
<script>
|
build/web/main.dart.js
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
build/web/manifest.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
{
|
2 |
-
"name": "
|
3 |
-
"short_name": "
|
4 |
"start_url": ".",
|
5 |
"display": "standalone",
|
6 |
"background_color": "#0175C2",
|
|
|
1 |
{
|
2 |
+
"name": "tikslop",
|
3 |
+
"short_name": "tikslop",
|
4 |
"start_url": ".",
|
5 |
"display": "standalone",
|
6 |
"background_color": "#0175C2",
|
build/web/{aitube.png → tikslop.png}
RENAMED
File without changes
|
build/web/{aitube.svg → tikslop.svg}
RENAMED
File without changes
|
build/web/version.json
CHANGED
@@ -1 +1 @@
|
|
1 |
-
{"app_name":"
|
|
|
1 |
+
{"app_name":"tikslop","version":"1.0.0","build_number":"1","package_name":"tikslop"}
|
ios/Runner.xcodeproj/project.pbxproj
CHANGED
@@ -368,7 +368,7 @@
|
|
368 |
"$(inherited)",
|
369 |
"@executable_path/Frameworks",
|
370 |
);
|
371 |
-
PRODUCT_BUNDLE_IDENTIFIER = com.example.
|
372 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
373 |
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
374 |
SWIFT_VERSION = 5.0;
|
@@ -384,7 +384,7 @@
|
|
384 |
CURRENT_PROJECT_VERSION = 1;
|
385 |
GENERATE_INFOPLIST_FILE = YES;
|
386 |
MARKETING_VERSION = 1.0;
|
387 |
-
PRODUCT_BUNDLE_IDENTIFIER = com.example.
|
388 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
389 |
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
390 |
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
@@ -401,7 +401,7 @@
|
|
401 |
CURRENT_PROJECT_VERSION = 1;
|
402 |
GENERATE_INFOPLIST_FILE = YES;
|
403 |
MARKETING_VERSION = 1.0;
|
404 |
-
PRODUCT_BUNDLE_IDENTIFIER = com.example.
|
405 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
406 |
SWIFT_VERSION = 5.0;
|
407 |
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
@@ -416,7 +416,7 @@
|
|
416 |
CURRENT_PROJECT_VERSION = 1;
|
417 |
GENERATE_INFOPLIST_FILE = YES;
|
418 |
MARKETING_VERSION = 1.0;
|
419 |
-
PRODUCT_BUNDLE_IDENTIFIER = com.example.
|
420 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
421 |
SWIFT_VERSION = 5.0;
|
422 |
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
@@ -547,7 +547,7 @@
|
|
547 |
"$(inherited)",
|
548 |
"@executable_path/Frameworks",
|
549 |
);
|
550 |
-
PRODUCT_BUNDLE_IDENTIFIER = com.example.
|
551 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
552 |
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
553 |
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
@@ -569,7 +569,7 @@
|
|
569 |
"$(inherited)",
|
570 |
"@executable_path/Frameworks",
|
571 |
);
|
572 |
-
PRODUCT_BUNDLE_IDENTIFIER = com.example.
|
573 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
574 |
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
575 |
SWIFT_VERSION = 5.0;
|
|
|
368 |
"$(inherited)",
|
369 |
"@executable_path/Frameworks",
|
370 |
);
|
371 |
+
PRODUCT_BUNDLE_IDENTIFIER = com.example.tikslop;
|
372 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
373 |
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
374 |
SWIFT_VERSION = 5.0;
|
|
|
384 |
CURRENT_PROJECT_VERSION = 1;
|
385 |
GENERATE_INFOPLIST_FILE = YES;
|
386 |
MARKETING_VERSION = 1.0;
|
387 |
+
PRODUCT_BUNDLE_IDENTIFIER = com.example.tikslop.RunnerTests;
|
388 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
389 |
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
390 |
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
|
|
401 |
CURRENT_PROJECT_VERSION = 1;
|
402 |
GENERATE_INFOPLIST_FILE = YES;
|
403 |
MARKETING_VERSION = 1.0;
|
404 |
+
PRODUCT_BUNDLE_IDENTIFIER = com.example.tikslop.RunnerTests;
|
405 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
406 |
SWIFT_VERSION = 5.0;
|
407 |
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
|
|
416 |
CURRENT_PROJECT_VERSION = 1;
|
417 |
GENERATE_INFOPLIST_FILE = YES;
|
418 |
MARKETING_VERSION = 1.0;
|
419 |
+
PRODUCT_BUNDLE_IDENTIFIER = com.example.tikslop.RunnerTests;
|
420 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
421 |
SWIFT_VERSION = 5.0;
|
422 |
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
|
|
547 |
"$(inherited)",
|
548 |
"@executable_path/Frameworks",
|
549 |
);
|
550 |
+
PRODUCT_BUNDLE_IDENTIFIER = com.example.tikslop;
|
551 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
552 |
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
553 |
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
|
|
569 |
"$(inherited)",
|
570 |
"@executable_path/Frameworks",
|
571 |
);
|
572 |
+
PRODUCT_BUNDLE_IDENTIFIER = com.example.tikslop;
|
573 |
PRODUCT_NAME = "$(TARGET_NAME)";
|
574 |
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
575 |
SWIFT_VERSION = 5.0;
|
ios/Runner/Info.plist
CHANGED
@@ -5,7 +5,7 @@
|
|
5 |
<key>CFBundleDevelopmentRegion</key>
|
6 |
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
7 |
<key>CFBundleDisplayName</key>
|
8 |
-
<string>
|
9 |
<key>CFBundleExecutable</key>
|
10 |
<string>$(EXECUTABLE_NAME)</string>
|
11 |
<key>CFBundleIdentifier</key>
|
@@ -13,7 +13,7 @@
|
|
13 |
<key>CFBundleInfoDictionaryVersion</key>
|
14 |
<string>6.0</string>
|
15 |
<key>CFBundleName</key>
|
16 |
-
<string>
|
17 |
<key>CFBundlePackageType</key>
|
18 |
<string>APPL</string>
|
19 |
<key>CFBundleShortVersionString</key>
|
|
|
5 |
<key>CFBundleDevelopmentRegion</key>
|
6 |
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
7 |
<key>CFBundleDisplayName</key>
|
8 |
+
<string>TikSlop</string>
|
9 |
<key>CFBundleExecutable</key>
|
10 |
<string>$(EXECUTABLE_NAME)</string>
|
11 |
<key>CFBundleIdentifier</key>
|
|
|
13 |
<key>CFBundleInfoDictionaryVersion</key>
|
14 |
<string>6.0</string>
|
15 |
<key>CFBundleName</key>
|
16 |
+
<string>tikslop</string>
|
17 |
<key>CFBundlePackageType</key>
|
18 |
<string>APPL</string>
|
19 |
<key>CFBundleShortVersionString</key>
|
lib/config/README.md
CHANGED
@@ -3,10 +3,10 @@ Put secrets in here, used only during development
|
|
3 |
eg
|
4 |
|
5 |
```
|
6 |
-
class
|
7 |
final String huggingfaceToken;
|
8 |
|
9 |
-
|
10 |
this.huggingfaceToken = '',
|
11 |
})
|
12 |
}
|
|
|
3 |
eg
|
4 |
|
5 |
```
|
6 |
+
class TikSlopSecrets {
|
7 |
final String huggingfaceToken;
|
8 |
|
9 |
+
TikSlopSecrets({
|
10 |
this.huggingfaceToken = '',
|
11 |
})
|
12 |
}
|
lib/config/config.dart
CHANGED
@@ -20,7 +20,7 @@ class Configuration {
|
|
20 |
// Get custom config path from environment
|
21 |
const customConfigPath = String.fromEnvironment(
|
22 |
'CONFIG_PATH',
|
23 |
-
defaultValue: 'assets/config/
|
24 |
);
|
25 |
|
26 |
try {
|
|
|
20 |
// Get custom config path from environment
|
21 |
const customConfigPath = String.fromEnvironment(
|
22 |
'CONFIG_PATH',
|
23 |
+
defaultValue: 'assets/config/tikslop.yaml'
|
24 |
);
|
25 |
|
26 |
try {
|
lib/config/secrets.sample.dart
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
/// SECRETS - KEEP THIS OUT OF GIT
|
2 |
-
class
|
3 |
static const huggingfaceToken = '<HIDDEN FOR CONFIDENTIALITY>';
|
4 |
}
|
|
|
1 |
/// SECRETS - KEEP THIS OUT OF GIT
|
2 |
+
class TikSlopSecrets {
|
3 |
static const huggingfaceToken = '<HIDDEN FOR CONFIDENTIALITY>';
|
4 |
}
|
lib/main.dart
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
// lib/main.dart
|
2 |
-
import 'package:
|
3 |
-
import 'package:
|
4 |
-
import 'package:
|
5 |
-
import 'package:
|
6 |
-
import 'package:
|
7 |
-
import 'package:
|
8 |
-
import 'package:
|
9 |
-
import 'package:
|
10 |
import 'package:flutter/foundation.dart';
|
11 |
import 'package:flutter/material.dart';
|
12 |
import 'screens/home_screen.dart';
|
@@ -91,7 +91,7 @@ void main() async {
|
|
91 |
wsService.statusStream.listen((status) {
|
92 |
if (status == ConnectionStatus.maintenance) {
|
93 |
// Force update to maintenance screen if server goes into maintenance mode later
|
94 |
-
runApp(
|
95 |
}
|
96 |
});
|
97 |
|
@@ -107,13 +107,13 @@ void main() async {
|
|
107 |
}
|
108 |
}
|
109 |
|
110 |
-
runApp(
|
111 |
}
|
112 |
|
113 |
-
class
|
114 |
final Widget home;
|
115 |
|
116 |
-
const
|
117 |
|
118 |
@override
|
119 |
Widget build(BuildContext context) {
|
@@ -121,56 +121,56 @@ class AiTubeApp extends StatelessWidget {
|
|
121 |
title: Configuration.instance.uiProductName,
|
122 |
theme: ThemeData.dark().copyWith(
|
123 |
colorScheme: const ColorScheme.dark(
|
124 |
-
surface:
|
125 |
-
surfaceContainerHighest:
|
126 |
-
primary:
|
127 |
-
onSurface:
|
128 |
-
onSurfaceVariant:
|
129 |
),
|
130 |
-
scaffoldBackgroundColor:
|
131 |
cardTheme: CardThemeData(
|
132 |
-
color:
|
133 |
elevation: 0,
|
134 |
shape: RoundedRectangleBorder(
|
135 |
borderRadius: BorderRadius.circular(12),
|
136 |
),
|
137 |
),
|
138 |
appBarTheme: const AppBarTheme(
|
139 |
-
backgroundColor:
|
140 |
elevation: 0,
|
141 |
),
|
142 |
textTheme: const TextTheme(
|
143 |
-
titleLarge: TextStyle(color:
|
144 |
-
titleMedium: TextStyle(color:
|
145 |
-
bodyLarge: TextStyle(color:
|
146 |
-
bodyMedium: TextStyle(color:
|
147 |
),
|
148 |
),
|
149 |
darkTheme: ThemeData.dark().copyWith(
|
150 |
colorScheme: const ColorScheme.dark(
|
151 |
-
surface:
|
152 |
-
surfaceContainerHighest:
|
153 |
-
primary:
|
154 |
-
onSurface:
|
155 |
-
onSurfaceVariant:
|
156 |
),
|
157 |
-
scaffoldBackgroundColor:
|
158 |
cardTheme: CardThemeData(
|
159 |
-
color:
|
160 |
elevation: 0,
|
161 |
shape: RoundedRectangleBorder(
|
162 |
borderRadius: BorderRadius.circular(12),
|
163 |
),
|
164 |
),
|
165 |
appBarTheme: const AppBarTheme(
|
166 |
-
backgroundColor:
|
167 |
elevation: 0,
|
168 |
),
|
169 |
textTheme: const TextTheme(
|
170 |
-
titleLarge: TextStyle(color:
|
171 |
-
titleMedium: TextStyle(color:
|
172 |
-
bodyLarge: TextStyle(color:
|
173 |
-
bodyMedium: TextStyle(color:
|
174 |
),
|
175 |
),
|
176 |
// Use custom route handling to support deep linking with URL parameters on web
|
|
|
1 |
// lib/main.dart
|
2 |
+
import 'package:tikslop/config/config.dart';
|
3 |
+
import 'package:tikslop/models/video_result.dart';
|
4 |
+
import 'package:tikslop/screens/video_screen.dart';
|
5 |
+
import 'package:tikslop/services/settings_service.dart';
|
6 |
+
import 'package:tikslop/services/websocket_api_service.dart';
|
7 |
+
import 'package:tikslop/theme/colors.dart';
|
8 |
+
import 'package:tikslop/widgets/maintenance_screen.dart';
|
9 |
+
import 'package:tikslop/widgets/web_utils.dart';
|
10 |
import 'package:flutter/foundation.dart';
|
11 |
import 'package:flutter/material.dart';
|
12 |
import 'screens/home_screen.dart';
|
|
|
91 |
wsService.statusStream.listen((status) {
|
92 |
if (status == ConnectionStatus.maintenance) {
|
93 |
// Force update to maintenance screen if server goes into maintenance mode later
|
94 |
+
runApp(TikSlopApp(home: const MaintenanceScreen(error: null)));
|
95 |
}
|
96 |
});
|
97 |
|
|
|
107 |
}
|
108 |
}
|
109 |
|
110 |
+
runApp(TikSlopApp(home: homeWidget));
|
111 |
}
|
112 |
|
113 |
+
class TikSlopApp extends StatelessWidget {
|
114 |
final Widget home;
|
115 |
|
116 |
+
const TikSlopApp({super.key, required this.home});
|
117 |
|
118 |
@override
|
119 |
Widget build(BuildContext context) {
|
|
|
121 |
title: Configuration.instance.uiProductName,
|
122 |
theme: ThemeData.dark().copyWith(
|
123 |
colorScheme: const ColorScheme.dark(
|
124 |
+
surface: TikSlopColors.surface,
|
125 |
+
surfaceContainerHighest: TikSlopColors.surfaceVariant,
|
126 |
+
primary: TikSlopColors.primary,
|
127 |
+
onSurface: TikSlopColors.onSurface,
|
128 |
+
onSurfaceVariant: TikSlopColors.onSurfaceVariant,
|
129 |
),
|
130 |
+
scaffoldBackgroundColor: TikSlopColors.background,
|
131 |
cardTheme: CardThemeData(
|
132 |
+
color: TikSlopColors.surface,
|
133 |
elevation: 0,
|
134 |
shape: RoundedRectangleBorder(
|
135 |
borderRadius: BorderRadius.circular(12),
|
136 |
),
|
137 |
),
|
138 |
appBarTheme: const AppBarTheme(
|
139 |
+
backgroundColor: TikSlopColors.background,
|
140 |
elevation: 0,
|
141 |
),
|
142 |
textTheme: const TextTheme(
|
143 |
+
titleLarge: TextStyle(color: TikSlopColors.onBackground),
|
144 |
+
titleMedium: TextStyle(color: TikSlopColors.onBackground),
|
145 |
+
bodyLarge: TextStyle(color: TikSlopColors.onSurface),
|
146 |
+
bodyMedium: TextStyle(color: TikSlopColors.onSurfaceVariant),
|
147 |
),
|
148 |
),
|
149 |
darkTheme: ThemeData.dark().copyWith(
|
150 |
colorScheme: const ColorScheme.dark(
|
151 |
+
surface: TikSlopColors.surface,
|
152 |
+
surfaceContainerHighest: TikSlopColors.surfaceVariant,
|
153 |
+
primary: TikSlopColors.primary,
|
154 |
+
onSurface: TikSlopColors.onSurface,
|
155 |
+
onSurfaceVariant: TikSlopColors.onSurfaceVariant,
|
156 |
),
|
157 |
+
scaffoldBackgroundColor: TikSlopColors.background,
|
158 |
cardTheme: CardThemeData(
|
159 |
+
color: TikSlopColors.surface,
|
160 |
elevation: 0,
|
161 |
shape: RoundedRectangleBorder(
|
162 |
borderRadius: BorderRadius.circular(12),
|
163 |
),
|
164 |
),
|
165 |
appBarTheme: const AppBarTheme(
|
166 |
+
backgroundColor: TikSlopColors.background,
|
167 |
elevation: 0,
|
168 |
),
|
169 |
textTheme: const TextTheme(
|
170 |
+
titleLarge: TextStyle(color: TikSlopColors.onBackground),
|
171 |
+
titleMedium: TextStyle(color: TikSlopColors.onBackground),
|
172 |
+
bodyLarge: TextStyle(color: TikSlopColors.onSurface),
|
173 |
+
bodyMedium: TextStyle(color: TikSlopColors.onSurfaceVariant),
|
174 |
),
|
175 |
),
|
176 |
// Use custom route handling to support deep linking with URL parameters on web
|
lib/screens/home_screen.dart
CHANGED
@@ -1,18 +1,18 @@
|
|
1 |
// lib/screens/home_screen.dart
|
2 |
import 'dart:async';
|
3 |
-
import 'package:
|
4 |
-
import 'package:
|
5 |
import 'package:flutter/foundation.dart';
|
6 |
import 'package:flutter/material.dart';
|
7 |
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
8 |
-
import 'package:
|
9 |
-
import 'package:
|
10 |
-
import 'package:
|
11 |
-
import 'package:
|
12 |
-
import 'package:
|
13 |
-
import 'package:
|
14 |
-
import 'package:
|
15 |
-
import 'package:
|
16 |
|
17 |
class HomeScreen extends StatefulWidget {
|
18 |
final String? initialSearchQuery;
|
@@ -148,7 +148,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|
148 |
title: const Text(
|
149 |
'Connection Limit Reached',
|
150 |
style: TextStyle(
|
151 |
-
color:
|
152 |
fontSize: 20,
|
153 |
fontWeight: FontWeight.bold,
|
154 |
),
|
@@ -161,12 +161,12 @@ class _HomeScreenState extends State<HomeScreen> {
|
|
161 |
_websocketService.anonLimitMessage.isNotEmpty
|
162 |
? _websocketService.anonLimitMessage
|
163 |
: 'Anonymous users can enjoy 1 stream per IP address. If you are on a shared IP please enter your HF token, thank you!',
|
164 |
-
style: const TextStyle(color:
|
165 |
),
|
166 |
const SizedBox(height: 16),
|
167 |
const Text(
|
168 |
'Enter your HuggingFace API token to continue:',
|
169 |
-
style: TextStyle(color:
|
170 |
),
|
171 |
const SizedBox(height: 8),
|
172 |
TextField(
|
@@ -174,11 +174,11 @@ class _HomeScreenState extends State<HomeScreen> {
|
|
174 |
obscureText: obscureText,
|
175 |
decoration: InputDecoration(
|
176 |
labelText: 'API Key',
|
177 |
-
labelStyle: const TextStyle(color:
|
178 |
suffixIcon: IconButton(
|
179 |
icon: Icon(
|
180 |
obscureText ? Icons.visibility : Icons.visibility_off,
|
181 |
-
color:
|
182 |
),
|
183 |
onPressed: () => setState(() => obscureText = !obscureText),
|
184 |
),
|
@@ -189,19 +189,19 @@ class _HomeScreenState extends State<HomeScreen> {
|
|
189 |
),
|
190 |
],
|
191 |
),
|
192 |
-
backgroundColor:
|
193 |
actions: [
|
194 |
TextButton(
|
195 |
onPressed: () => Navigator.pop(dialogContext),
|
196 |
child: const Text(
|
197 |
'Cancel',
|
198 |
-
style: TextStyle(color:
|
199 |
),
|
200 |
),
|
201 |
FilledButton(
|
202 |
onPressed: () => Navigator.pop(dialogContext, controller.text),
|
203 |
style: FilledButton.styleFrom(
|
204 |
-
backgroundColor:
|
205 |
),
|
206 |
child: const Text('Save'),
|
207 |
),
|
@@ -237,7 +237,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|
237 |
title: const Text(
|
238 |
'Too Many Connections',
|
239 |
style: TextStyle(
|
240 |
-
color:
|
241 |
fontSize: 20,
|
242 |
fontWeight: FontWeight.bold,
|
243 |
),
|
@@ -248,16 +248,16 @@ class _HomeScreenState extends State<HomeScreen> {
|
|
248 |
children: [
|
249 |
Text(
|
250 |
_websocketService.deviceLimitMessage,
|
251 |
-
style: const TextStyle(color:
|
252 |
),
|
253 |
const SizedBox(height: 16),
|
254 |
const Text(
|
255 |
-
'Please close some of your other browser tabs running
|
256 |
-
style: TextStyle(color:
|
257 |
),
|
258 |
],
|
259 |
),
|
260 |
-
backgroundColor:
|
261 |
actions: [
|
262 |
FilledButton(
|
263 |
onPressed: () {
|
@@ -271,7 +271,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|
271 |
}
|
272 |
},
|
273 |
style: FilledButton.styleFrom(
|
274 |
-
backgroundColor:
|
275 |
),
|
276 |
child: const Text('Try Again'),
|
277 |
),
|
@@ -411,7 +411,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|
411 |
return Scaffold(
|
412 |
appBar: AppBar(
|
413 |
title: Text(Configuration.instance.uiProductName),
|
414 |
-
backgroundColor:
|
415 |
actions: [
|
416 |
Padding(
|
417 |
padding: const EdgeInsets.only(right: 8),
|
@@ -452,7 +452,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|
452 |
? 'Hallucinating search results using AI...'
|
453 |
: 'Results are generated on demand, videos rendered on the fly.',
|
454 |
style: const TextStyle(
|
455 |
-
color:
|
456 |
fontSize: 20
|
457 |
),
|
458 |
textAlign: TextAlign.center,
|
|
|
1 |
// lib/screens/home_screen.dart
|
2 |
import 'dart:async';
|
3 |
+
import 'package:tikslop/config/config.dart';
|
4 |
+
import 'package:tikslop/widgets/web_utils.dart';
|
5 |
import 'package:flutter/foundation.dart';
|
6 |
import 'package:flutter/material.dart';
|
7 |
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
8 |
+
import 'package:tikslop/screens/video_screen.dart';
|
9 |
+
import 'package:tikslop/screens/settings_screen.dart';
|
10 |
+
import 'package:tikslop/models/video_result.dart';
|
11 |
+
import 'package:tikslop/services/websocket_api_service.dart';
|
12 |
+
import 'package:tikslop/services/settings_service.dart';
|
13 |
+
import 'package:tikslop/widgets/video_card.dart';
|
14 |
+
import 'package:tikslop/widgets/search_box.dart';
|
15 |
+
import 'package:tikslop/theme/colors.dart';
|
16 |
|
17 |
class HomeScreen extends StatefulWidget {
|
18 |
final String? initialSearchQuery;
|
|
|
148 |
title: const Text(
|
149 |
'Connection Limit Reached',
|
150 |
style: TextStyle(
|
151 |
+
color: TikSlopColors.onBackground,
|
152 |
fontSize: 20,
|
153 |
fontWeight: FontWeight.bold,
|
154 |
),
|
|
|
161 |
_websocketService.anonLimitMessage.isNotEmpty
|
162 |
? _websocketService.anonLimitMessage
|
163 |
: 'Anonymous users can enjoy 1 stream per IP address. If you are on a shared IP please enter your HF token, thank you!',
|
164 |
+
style: const TextStyle(color: TikSlopColors.onSurface),
|
165 |
),
|
166 |
const SizedBox(height: 16),
|
167 |
const Text(
|
168 |
'Enter your HuggingFace API token to continue:',
|
169 |
+
style: TextStyle(color: TikSlopColors.onSurface),
|
170 |
),
|
171 |
const SizedBox(height: 8),
|
172 |
TextField(
|
|
|
174 |
obscureText: obscureText,
|
175 |
decoration: InputDecoration(
|
176 |
labelText: 'API Key',
|
177 |
+
labelStyle: const TextStyle(color: TikSlopColors.onSurfaceVariant),
|
178 |
suffixIcon: IconButton(
|
179 |
icon: Icon(
|
180 |
obscureText ? Icons.visibility : Icons.visibility_off,
|
181 |
+
color: TikSlopColors.onSurfaceVariant,
|
182 |
),
|
183 |
onPressed: () => setState(() => obscureText = !obscureText),
|
184 |
),
|
|
|
189 |
),
|
190 |
],
|
191 |
),
|
192 |
+
backgroundColor: TikSlopColors.surface,
|
193 |
actions: [
|
194 |
TextButton(
|
195 |
onPressed: () => Navigator.pop(dialogContext),
|
196 |
child: const Text(
|
197 |
'Cancel',
|
198 |
+
style: TextStyle(color: TikSlopColors.onSurfaceVariant),
|
199 |
),
|
200 |
),
|
201 |
FilledButton(
|
202 |
onPressed: () => Navigator.pop(dialogContext, controller.text),
|
203 |
style: FilledButton.styleFrom(
|
204 |
+
backgroundColor: TikSlopColors.primary,
|
205 |
),
|
206 |
child: const Text('Save'),
|
207 |
),
|
|
|
237 |
title: const Text(
|
238 |
'Too Many Connections',
|
239 |
style: TextStyle(
|
240 |
+
color: TikSlopColors.onBackground,
|
241 |
fontSize: 20,
|
242 |
fontWeight: FontWeight.bold,
|
243 |
),
|
|
|
248 |
children: [
|
249 |
Text(
|
250 |
_websocketService.deviceLimitMessage,
|
251 |
+
style: const TextStyle(color: TikSlopColors.onSurface),
|
252 |
),
|
253 |
const SizedBox(height: 16),
|
254 |
const Text(
|
255 |
+
'Please close some of your other browser tabs running TikSlop to continue.',
|
256 |
+
style: TextStyle(color: TikSlopColors.onSurface),
|
257 |
),
|
258 |
],
|
259 |
),
|
260 |
+
backgroundColor: TikSlopColors.surface,
|
261 |
actions: [
|
262 |
FilledButton(
|
263 |
onPressed: () {
|
|
|
271 |
}
|
272 |
},
|
273 |
style: FilledButton.styleFrom(
|
274 |
+
backgroundColor: TikSlopColors.primary,
|
275 |
),
|
276 |
child: const Text('Try Again'),
|
277 |
),
|
|
|
411 |
return Scaffold(
|
412 |
appBar: AppBar(
|
413 |
title: Text(Configuration.instance.uiProductName),
|
414 |
+
backgroundColor: TikSlopColors.background,
|
415 |
actions: [
|
416 |
Padding(
|
417 |
padding: const EdgeInsets.only(right: 8),
|
|
|
452 |
? 'Hallucinating search results using AI...'
|
453 |
: 'Results are generated on demand, videos rendered on the fly.',
|
454 |
style: const TextStyle(
|
455 |
+
color: TikSlopColors.onSurfaceVariant,
|
456 |
fontSize: 20
|
457 |
),
|
458 |
textAlign: TextAlign.center,
|
lib/screens/settings_screen.dart
CHANGED
@@ -51,7 +51,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|
51 |
const Text(
|
52 |
'API Configuration',
|
53 |
style: TextStyle(
|
54 |
-
color:
|
55 |
fontSize: 20,
|
56 |
fontWeight: FontWeight.bold,
|
57 |
),
|
@@ -127,7 +127,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|
127 |
const Text(
|
128 |
'Video Generation',
|
129 |
style: TextStyle(
|
130 |
-
color:
|
131 |
fontSize: 20,
|
132 |
fontWeight: FontWeight.bold,
|
133 |
),
|
@@ -171,7 +171,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|
171 |
const Text(
|
172 |
'Custom Video Model',
|
173 |
style: TextStyle(
|
174 |
-
color:
|
175 |
fontSize: 20,
|
176 |
fontWeight: FontWeight.bold,
|
177 |
),
|
@@ -192,7 +192,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|
192 |
),
|
193 |
const SizedBox(height: 8),
|
194 |
const Text(
|
195 |
-
'Interested in using custom Hugging Face models? If you trained a public and distilled LoRA model based on LTX-Video 0.9.6 (remember, it has to be distilled), it can be integrated into
|
196 |
style: TextStyle(
|
197 |
fontSize: 12,
|
198 |
color: Colors.grey,
|
|
|
51 |
const Text(
|
52 |
'API Configuration',
|
53 |
style: TextStyle(
|
54 |
+
color: TikSlopColors.onBackground,
|
55 |
fontSize: 20,
|
56 |
fontWeight: FontWeight.bold,
|
57 |
),
|
|
|
127 |
const Text(
|
128 |
'Video Generation',
|
129 |
style: TextStyle(
|
130 |
+
color: TikSlopColors.onBackground,
|
131 |
fontSize: 20,
|
132 |
fontWeight: FontWeight.bold,
|
133 |
),
|
|
|
171 |
const Text(
|
172 |
'Custom Video Model',
|
173 |
style: TextStyle(
|
174 |
+
color: TikSlopColors.onBackground,
|
175 |
fontSize: 20,
|
176 |
fontWeight: FontWeight.bold,
|
177 |
),
|
|
|
192 |
),
|
193 |
const SizedBox(height: 8),
|
194 |
const Text(
|
195 |
+
'Interested in using custom Hugging Face models? If you trained a public and distilled LoRA model based on LTX-Video 0.9.6 (remember, it has to be distilled), it can be integrated into TikSlop. Please open a thread in the Community forum and I\'ll see for a way to allow for custom models.',
|
196 |
style: TextStyle(
|
197 |
fontSize: 12,
|
198 |
color: Colors.grey,
|
lib/screens/video_screen.dart
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
// lib/screens/video_screen.dart
|
2 |
import 'dart:async';
|
3 |
|
4 |
-
import 'package:
|
5 |
-
import 'package:
|
6 |
-
import 'package:
|
7 |
-
import 'package:
|
8 |
import 'package:flutter/foundation.dart';
|
9 |
import 'package:flutter/material.dart';
|
10 |
import 'package:flutter/services.dart';
|
11 |
-
import 'package:universal_html/html.dart' if (dart.library.io) 'package:
|
12 |
import '../config/config.dart';
|
13 |
import '../models/video_result.dart';
|
14 |
import '../services/websocket_api_service.dart';
|
@@ -114,7 +114,7 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
114 |
title: const Text(
|
115 |
'Connection Limit Reached',
|
116 |
style: TextStyle(
|
117 |
-
color:
|
118 |
fontSize: 20,
|
119 |
fontWeight: FontWeight.bold,
|
120 |
),
|
@@ -127,12 +127,12 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
127 |
_websocketService.anonLimitMessage.isNotEmpty
|
128 |
? _websocketService.anonLimitMessage
|
129 |
: 'Anonymous users can enjoy 1 stream per IP address. If you are on a shared IP please enter your HF token, thank you!',
|
130 |
-
style: const TextStyle(color:
|
131 |
),
|
132 |
const SizedBox(height: 16),
|
133 |
const Text(
|
134 |
'Enter your HuggingFace API token to continue:',
|
135 |
-
style: TextStyle(color:
|
136 |
),
|
137 |
const SizedBox(height: 8),
|
138 |
TextField(
|
@@ -140,11 +140,11 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
140 |
obscureText: obscureText,
|
141 |
decoration: InputDecoration(
|
142 |
labelText: 'API Key',
|
143 |
-
labelStyle: const TextStyle(color:
|
144 |
suffixIcon: IconButton(
|
145 |
icon: Icon(
|
146 |
obscureText ? Icons.visibility : Icons.visibility_off,
|
147 |
-
color:
|
148 |
),
|
149 |
onPressed: () => setState(() => obscureText = !obscureText),
|
150 |
),
|
@@ -155,19 +155,19 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
155 |
),
|
156 |
],
|
157 |
),
|
158 |
-
backgroundColor:
|
159 |
actions: [
|
160 |
TextButton(
|
161 |
onPressed: () => Navigator.pop(dialogContext),
|
162 |
child: const Text(
|
163 |
'Cancel',
|
164 |
-
style: TextStyle(color:
|
165 |
),
|
166 |
),
|
167 |
FilledButton(
|
168 |
onPressed: () => Navigator.pop(dialogContext, controller.text),
|
169 |
style: FilledButton.styleFrom(
|
170 |
-
backgroundColor:
|
171 |
),
|
172 |
child: const Text('Save'),
|
173 |
),
|
@@ -203,7 +203,7 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
203 |
title: const Text(
|
204 |
'Too Many Connections',
|
205 |
style: TextStyle(
|
206 |
-
color:
|
207 |
fontSize: 20,
|
208 |
fontWeight: FontWeight.bold,
|
209 |
),
|
@@ -214,16 +214,16 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
214 |
children: [
|
215 |
Text(
|
216 |
_websocketService.deviceLimitMessage,
|
217 |
-
style: const TextStyle(color:
|
218 |
),
|
219 |
const SizedBox(height: 16),
|
220 |
const Text(
|
221 |
-
'Please close some of your other browser tabs running
|
222 |
-
style: TextStyle(color:
|
223 |
),
|
224 |
],
|
225 |
),
|
226 |
-
backgroundColor:
|
227 |
actions: [
|
228 |
FilledButton(
|
229 |
onPressed: () {
|
@@ -237,7 +237,7 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
237 |
}
|
238 |
},
|
239 |
style: FilledButton.styleFrom(
|
240 |
-
backgroundColor:
|
241 |
),
|
242 |
child: const Text('Try Again'),
|
243 |
),
|
@@ -265,7 +265,7 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
265 |
void _shareVideo() async {
|
266 |
|
267 |
// For non-web platforms
|
268 |
-
final uri = Uri.parse("https://
|
269 |
final params = Map<String, String>.from(uri.queryParameters);
|
270 |
|
271 |
// Ensure title and description are in the URL parameters
|
@@ -279,7 +279,7 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
279 |
|
280 |
try {
|
281 |
// this is a text to share on social media
|
282 |
-
// final textToCopy = 'Messing around with #
|
283 |
|
284 |
// but for now let's just use the url
|
285 |
final textToCopy = shareUrl;
|
@@ -372,8 +372,8 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
372 |
child: AppBar(
|
373 |
leading: IconButton(
|
374 |
icon: Navigator.canPop(context)
|
375 |
-
? const Icon(Icons.arrow_back, color:
|
376 |
-
: const Icon(Icons.home, color:
|
377 |
onPressed: () {
|
378 |
// Restore the search parameter in URL when navigating back
|
379 |
if (kIsWeb) {
|
@@ -505,21 +505,21 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
505 |
child: Text(
|
506 |
_videoData.title,
|
507 |
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
508 |
-
color:
|
509 |
fontWeight: FontWeight.bold,
|
510 |
fontSize: 18
|
511 |
),
|
512 |
),
|
513 |
),
|
514 |
IconButton(
|
515 |
-
icon: const Icon(Icons.share, color:
|
516 |
onPressed: _shareVideo,
|
517 |
tooltip: 'Share this creation',
|
518 |
),
|
519 |
],
|
520 |
),
|
521 |
-
iconColor:
|
522 |
-
collapsedIconColor:
|
523 |
backgroundColor: Colors.transparent,
|
524 |
collapsedBackgroundColor: Colors.transparent,
|
525 |
children: [
|
@@ -531,7 +531,7 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
531 |
Text(
|
532 |
_videoData.description,
|
533 |
style: const TextStyle(
|
534 |
-
color:
|
535 |
height: 1.5,
|
536 |
),
|
537 |
),
|
|
|
1 |
// lib/screens/video_screen.dart
|
2 |
import 'dart:async';
|
3 |
|
4 |
+
import 'package:tikslop/screens/home_screen.dart';
|
5 |
+
import 'package:tikslop/widgets/chat_widget.dart';
|
6 |
+
import 'package:tikslop/widgets/search_box.dart';
|
7 |
+
import 'package:tikslop/widgets/web_utils.dart';
|
8 |
import 'package:flutter/foundation.dart';
|
9 |
import 'package:flutter/material.dart';
|
10 |
import 'package:flutter/services.dart';
|
11 |
+
import 'package:universal_html/html.dart' if (dart.library.io) 'package:tikslop/services/html_stub.dart' as html;
|
12 |
import '../config/config.dart';
|
13 |
import '../models/video_result.dart';
|
14 |
import '../services/websocket_api_service.dart';
|
|
|
114 |
title: const Text(
|
115 |
'Connection Limit Reached',
|
116 |
style: TextStyle(
|
117 |
+
color: TikSlopColors.onBackground,
|
118 |
fontSize: 20,
|
119 |
fontWeight: FontWeight.bold,
|
120 |
),
|
|
|
127 |
_websocketService.anonLimitMessage.isNotEmpty
|
128 |
? _websocketService.anonLimitMessage
|
129 |
: 'Anonymous users can enjoy 1 stream per IP address. If you are on a shared IP please enter your HF token, thank you!',
|
130 |
+
style: const TextStyle(color: TikSlopColors.onSurface),
|
131 |
),
|
132 |
const SizedBox(height: 16),
|
133 |
const Text(
|
134 |
'Enter your HuggingFace API token to continue:',
|
135 |
+
style: TextStyle(color: TikSlopColors.onSurface),
|
136 |
),
|
137 |
const SizedBox(height: 8),
|
138 |
TextField(
|
|
|
140 |
obscureText: obscureText,
|
141 |
decoration: InputDecoration(
|
142 |
labelText: 'API Key',
|
143 |
+
labelStyle: const TextStyle(color: TikSlopColors.onSurfaceVariant),
|
144 |
suffixIcon: IconButton(
|
145 |
icon: Icon(
|
146 |
obscureText ? Icons.visibility : Icons.visibility_off,
|
147 |
+
color: TikSlopColors.onSurfaceVariant,
|
148 |
),
|
149 |
onPressed: () => setState(() => obscureText = !obscureText),
|
150 |
),
|
|
|
155 |
),
|
156 |
],
|
157 |
),
|
158 |
+
backgroundColor: TikSlopColors.surface,
|
159 |
actions: [
|
160 |
TextButton(
|
161 |
onPressed: () => Navigator.pop(dialogContext),
|
162 |
child: const Text(
|
163 |
'Cancel',
|
164 |
+
style: TextStyle(color: TikSlopColors.onSurfaceVariant),
|
165 |
),
|
166 |
),
|
167 |
FilledButton(
|
168 |
onPressed: () => Navigator.pop(dialogContext, controller.text),
|
169 |
style: FilledButton.styleFrom(
|
170 |
+
backgroundColor: TikSlopColors.primary,
|
171 |
),
|
172 |
child: const Text('Save'),
|
173 |
),
|
|
|
203 |
title: const Text(
|
204 |
'Too Many Connections',
|
205 |
style: TextStyle(
|
206 |
+
color: TikSlopColors.onBackground,
|
207 |
fontSize: 20,
|
208 |
fontWeight: FontWeight.bold,
|
209 |
),
|
|
|
214 |
children: [
|
215 |
Text(
|
216 |
_websocketService.deviceLimitMessage,
|
217 |
+
style: const TextStyle(color: TikSlopColors.onSurface),
|
218 |
),
|
219 |
const SizedBox(height: 16),
|
220 |
const Text(
|
221 |
+
'Please close some of your other browser tabs running TikSlop to continue.',
|
222 |
+
style: TextStyle(color: TikSlopColors.onSurface),
|
223 |
),
|
224 |
],
|
225 |
),
|
226 |
+
backgroundColor: TikSlopColors.surface,
|
227 |
actions: [
|
228 |
FilledButton(
|
229 |
onPressed: () {
|
|
|
237 |
}
|
238 |
},
|
239 |
style: FilledButton.styleFrom(
|
240 |
+
backgroundColor: TikSlopColors.primary,
|
241 |
),
|
242 |
child: const Text('Try Again'),
|
243 |
),
|
|
|
265 |
void _shareVideo() async {
|
266 |
|
267 |
// For non-web platforms
|
268 |
+
final uri = Uri.parse("https://tikslop.at");
|
269 |
final params = Map<String, String>.from(uri.queryParameters);
|
270 |
|
271 |
// Ensure title and description are in the URL parameters
|
|
|
279 |
|
280 |
try {
|
281 |
// this is a text to share on social media
|
282 |
+
// final textToCopy = 'Messing around with #tikslop 👀 $shareUrl';
|
283 |
|
284 |
// but for now let's just use the url
|
285 |
final textToCopy = shareUrl;
|
|
|
372 |
child: AppBar(
|
373 |
leading: IconButton(
|
374 |
icon: Navigator.canPop(context)
|
375 |
+
? const Icon(Icons.arrow_back, color: TikSlopColors.onBackground)
|
376 |
+
: const Icon(Icons.home, color: TikSlopColors.onBackground),
|
377 |
onPressed: () {
|
378 |
// Restore the search parameter in URL when navigating back
|
379 |
if (kIsWeb) {
|
|
|
505 |
child: Text(
|
506 |
_videoData.title,
|
507 |
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
508 |
+
color: TikSlopColors.onBackground,
|
509 |
fontWeight: FontWeight.bold,
|
510 |
fontSize: 18
|
511 |
),
|
512 |
),
|
513 |
),
|
514 |
IconButton(
|
515 |
+
icon: const Icon(Icons.share, color: TikSlopColors.primary),
|
516 |
onPressed: _shareVideo,
|
517 |
tooltip: 'Share this creation',
|
518 |
),
|
519 |
],
|
520 |
),
|
521 |
+
iconColor: TikSlopColors.primary,
|
522 |
+
collapsedIconColor: TikSlopColors.primary,
|
523 |
backgroundColor: Colors.transparent,
|
524 |
collapsedBackgroundColor: Colors.transparent,
|
525 |
children: [
|
|
|
531 |
Text(
|
532 |
_videoData.description,
|
533 |
style: const TextStyle(
|
534 |
+
color: TikSlopColors.onSurface,
|
535 |
height: 1.5,
|
536 |
),
|
537 |
),
|
lib/services/chat_service.dart
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
// lib/services/chat_service.dart
|
3 |
import 'dart:async';
|
4 |
import 'dart:math';
|
5 |
-
import 'package:
|
6 |
import 'package:flutter/material.dart';
|
7 |
import 'package:shared_preferences/shared_preferences.dart';
|
8 |
import 'package:uuid/uuid.dart';
|
|
|
2 |
// lib/services/chat_service.dart
|
3 |
import 'dart:async';
|
4 |
import 'dart:math';
|
5 |
+
import 'package:tikslop/services/websocket_api_service.dart';
|
6 |
import 'package:flutter/material.dart';
|
7 |
import 'package:shared_preferences/shared_preferences.dart';
|
8 |
import 'package:uuid/uuid.dart';
|
lib/services/clip_queue/clip_generation_handler.dart
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
import 'dart:async';
|
4 |
import 'package:flutter/foundation.dart';
|
5 |
-
import 'package:
|
6 |
import '../websocket_api_service.dart';
|
7 |
import '../../models/video_result.dart';
|
8 |
import 'clip_states.dart';
|
|
|
2 |
|
3 |
import 'dart:async';
|
4 |
import 'package:flutter/foundation.dart';
|
5 |
+
import 'package:tikslop/config/config.dart';
|
6 |
import '../websocket_api_service.dart';
|
7 |
import '../../models/video_result.dart';
|
8 |
import 'clip_states.dart';
|
lib/services/clip_queue/clip_queue_manager.dart
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
import 'dart:async';
|
4 |
import 'dart:math';
|
5 |
-
import 'package:
|
6 |
import 'package:flutter/foundation.dart';
|
7 |
import 'package:collection/collection.dart';
|
8 |
import '../../models/video_result.dart';
|
|
|
2 |
|
3 |
import 'dart:async';
|
4 |
import 'dart:math';
|
5 |
+
import 'package:tikslop/config/config.dart';
|
6 |
import 'package:flutter/foundation.dart';
|
7 |
import 'package:collection/collection.dart';
|
8 |
import '../../models/video_result.dart';
|
lib/services/settings_service.dart
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import 'dart:async';
|
2 |
|
3 |
import 'package:shared_preferences/shared_preferences.dart';
|
4 |
-
import 'package:
|
5 |
|
6 |
class SettingsService {
|
7 |
static const String _promptPrefixKey = 'video_prompt_prefix';
|
|
|
1 |
import 'dart:async';
|
2 |
|
3 |
import 'package:shared_preferences/shared_preferences.dart';
|
4 |
+
import 'package:tikslop/config/config.dart';
|
5 |
|
6 |
class SettingsService {
|
7 |
static const String _promptPrefixKey = 'video_prompt_prefix';
|
lib/services/websocket_api_service.dart
CHANGED
@@ -2,13 +2,13 @@ import 'dart:async';
|
|
2 |
import 'dart:io' as io;
|
3 |
// For web platform, conditionally import http instead of HttpClient
|
4 |
import 'package:http/http.dart' as http;
|
5 |
-
import 'package:
|
6 |
import 'package:synchronized/synchronized.dart';
|
7 |
import 'dart:convert';
|
8 |
// Conditionally import html for web platform with proper handling
|
9 |
import 'html_stub.dart' if (dart.library.html) 'dart:html' as html;
|
10 |
-
import 'package:
|
11 |
-
import 'package:
|
12 |
import 'package:flutter/foundation.dart';
|
13 |
import 'package:uuid/uuid.dart';
|
14 |
import 'package:web_socket_channel/web_socket_channel.dart';
|
@@ -82,8 +82,8 @@ class WebSocketApiService {
|
|
82 |
const isProduction = bool.fromEnvironment('PRODUCTION_MODE', defaultValue: false);
|
83 |
|
84 |
if (isProduction) {
|
85 |
-
// Production default is
|
86 |
-
const productionUrl = 'wss://
|
87 |
debugPrint('WebSocketApiService: Using production WebSocket URL: $productionUrl');
|
88 |
return productionUrl;
|
89 |
} else {
|
@@ -219,7 +219,7 @@ class WebSocketApiService {
|
|
219 |
String get anonLimitMessage => _anonLimitMessage;
|
220 |
|
221 |
// Message to display when device limit is exceeded
|
222 |
-
String _deviceLimitMessage = 'Too many connections from this device. Please close other tabs running
|
223 |
String get deviceLimitMessage => _deviceLimitMessage;
|
224 |
|
225 |
// Stream to notify listeners when anonymous limit status changes
|
@@ -231,8 +231,8 @@ class WebSocketApiService {
|
|
231 |
Stream<bool> get deviceLimitStream => _deviceLimitController.stream;
|
232 |
|
233 |
// Constants for device connection limits
|
234 |
-
static const String _connectionCountKey = '
|
235 |
-
static const String _connectionIdKey = '
|
236 |
static const int _maxDeviceConnections = 3; // Maximum number of tabs/connections per device
|
237 |
static const Duration _connectionHeartbeatInterval = Duration(seconds: 10);
|
238 |
Timer? _connectionHeartbeatTimer;
|
|
|
2 |
import 'dart:io' as io;
|
3 |
// For web platform, conditionally import http instead of HttpClient
|
4 |
import 'package:http/http.dart' as http;
|
5 |
+
import 'package:tikslop/services/settings_service.dart';
|
6 |
import 'package:synchronized/synchronized.dart';
|
7 |
import 'dart:convert';
|
8 |
// Conditionally import html for web platform with proper handling
|
9 |
import 'html_stub.dart' if (dart.library.html) 'dart:html' as html;
|
10 |
+
import 'package:tikslop/config/config.dart';
|
11 |
+
import 'package:tikslop/models/chat_message.dart';
|
12 |
import 'package:flutter/foundation.dart';
|
13 |
import 'package:uuid/uuid.dart';
|
14 |
import 'package:web_socket_channel/web_socket_channel.dart';
|
|
|
82 |
const isProduction = bool.fromEnvironment('PRODUCTION_MODE', defaultValue: false);
|
83 |
|
84 |
if (isProduction) {
|
85 |
+
// Production default is tikslop.at
|
86 |
+
const productionUrl = 'wss://tikslop.at/ws';
|
87 |
debugPrint('WebSocketApiService: Using production WebSocket URL: $productionUrl');
|
88 |
return productionUrl;
|
89 |
} else {
|
|
|
219 |
String get anonLimitMessage => _anonLimitMessage;
|
220 |
|
221 |
// Message to display when device limit is exceeded
|
222 |
+
String _deviceLimitMessage = 'Too many connections from this device. Please close other tabs running TikSlop.';
|
223 |
String get deviceLimitMessage => _deviceLimitMessage;
|
224 |
|
225 |
// Stream to notify listeners when anonymous limit status changes
|
|
|
231 |
Stream<bool> get deviceLimitStream => _deviceLimitController.stream;
|
232 |
|
233 |
// Constants for device connection limits
|
234 |
+
static const String _connectionCountKey = 'tikslop_connection_count';
|
235 |
+
static const String _connectionIdKey = 'tikslop_connection_id';
|
236 |
static const int _maxDeviceConnections = 3; // Maximum number of tabs/connections per device
|
237 |
static const Duration _connectionHeartbeatInterval = Duration(seconds: 10);
|
238 |
Timer? _connectionHeartbeatTimer;
|
lib/theme/colors.dart
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
// lib/theme/Colors.dart
|
2 |
import 'package:flutter/material.dart';
|
3 |
|
4 |
-
class
|
5 |
static const transparent = Color(0x00000000);
|
6 |
static const background = Color(0xFF171717);
|
7 |
static const surface = Color(0xFF272727);
|
|
|
1 |
// lib/theme/Colors.dart
|
2 |
import 'package:flutter/material.dart';
|
3 |
|
4 |
+
class TikSlopColors {
|
5 |
static const transparent = Color(0x00000000);
|
6 |
static const background = Color(0xFF171717);
|
7 |
static const surface = Color(0xFF272727);
|
lib/widgets/api_key_dialog.dart
CHANGED
@@ -19,7 +19,7 @@ class _ApiKeyDialogState extends State<ApiKeyDialog> {
|
|
19 |
title: const Text(
|
20 |
'Enter HuggingFace API Key',
|
21 |
style: TextStyle(
|
22 |
-
color:
|
23 |
fontSize: 20,
|
24 |
fontWeight: FontWeight.bold,
|
25 |
),
|
@@ -29,29 +29,29 @@ class _ApiKeyDialogState extends State<ApiKeyDialog> {
|
|
29 |
obscureText: _obscureText,
|
30 |
decoration: InputDecoration(
|
31 |
labelText: 'API Key',
|
32 |
-
labelStyle: const TextStyle(color:
|
33 |
suffixIcon: IconButton(
|
34 |
icon: Icon(
|
35 |
_obscureText ? Icons.visibility : Icons.visibility_off,
|
36 |
-
color:
|
37 |
),
|
38 |
onPressed: () => setState(() => _obscureText = !_obscureText),
|
39 |
),
|
40 |
),
|
41 |
),
|
42 |
-
backgroundColor:
|
43 |
actions: [
|
44 |
TextButton(
|
45 |
onPressed: () => Navigator.pop(context),
|
46 |
child: const Text(
|
47 |
'Cancel',
|
48 |
-
style: TextStyle(color:
|
49 |
),
|
50 |
),
|
51 |
FilledButton(
|
52 |
onPressed: () => Navigator.pop(context, _controller.text),
|
53 |
style: FilledButton.styleFrom(
|
54 |
-
backgroundColor:
|
55 |
),
|
56 |
child: const Text('Save'),
|
57 |
),
|
|
|
19 |
title: const Text(
|
20 |
'Enter HuggingFace API Key',
|
21 |
style: TextStyle(
|
22 |
+
color: TikSlopColors.onBackground,
|
23 |
fontSize: 20,
|
24 |
fontWeight: FontWeight.bold,
|
25 |
),
|
|
|
29 |
obscureText: _obscureText,
|
30 |
decoration: InputDecoration(
|
31 |
labelText: 'API Key',
|
32 |
+
labelStyle: const TextStyle(color: TikSlopColors.onSurfaceVariant),
|
33 |
suffixIcon: IconButton(
|
34 |
icon: Icon(
|
35 |
_obscureText ? Icons.visibility : Icons.visibility_off,
|
36 |
+
color: TikSlopColors.onSurfaceVariant,
|
37 |
),
|
38 |
onPressed: () => setState(() => _obscureText = !_obscureText),
|
39 |
),
|
40 |
),
|
41 |
),
|
42 |
+
backgroundColor: TikSlopColors.surface,
|
43 |
actions: [
|
44 |
TextButton(
|
45 |
onPressed: () => Navigator.pop(context),
|
46 |
child: const Text(
|
47 |
'Cancel',
|
48 |
+
style: TextStyle(color: TikSlopColors.onSurfaceVariant),
|
49 |
),
|
50 |
),
|
51 |
FilledButton(
|
52 |
onPressed: () => Navigator.pop(context, _controller.text),
|
53 |
style: FilledButton.styleFrom(
|
54 |
+
backgroundColor: TikSlopColors.primary,
|
55 |
),
|
56 |
child: const Text('Save'),
|
57 |
),
|
lib/widgets/chat_widget.dart
CHANGED
@@ -156,7 +156,7 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
156 |
child: Text(
|
157 |
'No messages yet',
|
158 |
style: TextStyle(
|
159 |
-
color:
|
160 |
fontSize: 14,
|
161 |
),
|
162 |
),
|
@@ -204,7 +204,7 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
204 |
Text(
|
205 |
message.username,
|
206 |
style: const TextStyle(
|
207 |
-
color:
|
208 |
fontWeight: FontWeight.bold,
|
209 |
),
|
210 |
),
|
@@ -212,7 +212,7 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
212 |
Text(
|
213 |
_formatTime(message.timestamp),
|
214 |
style: const TextStyle(
|
215 |
-
color:
|
216 |
fontSize: 12,
|
217 |
),
|
218 |
),
|
@@ -221,7 +221,7 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
221 |
const SizedBox(height: 4),
|
222 |
Text(
|
223 |
message.content,
|
224 |
-
style: const TextStyle(color:
|
225 |
),
|
226 |
],
|
227 |
),
|
@@ -235,10 +235,10 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
235 |
return Container(
|
236 |
padding: const EdgeInsets.all(8),
|
237 |
decoration: const BoxDecoration(
|
238 |
-
color:
|
239 |
border: Border(
|
240 |
top: BorderSide(
|
241 |
-
color:
|
242 |
width: 1,
|
243 |
),
|
244 |
),
|
@@ -248,7 +248,7 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
248 |
Expanded(
|
249 |
child: TextField(
|
250 |
controller: _messageController,
|
251 |
-
style: const TextStyle(color:
|
252 |
maxLength: 255,
|
253 |
maxLines: 1,
|
254 |
onChanged: (value) {
|
@@ -261,8 +261,8 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
261 |
}
|
262 |
},
|
263 |
decoration: InputDecoration(
|
264 |
-
hintText: 'Chat with this
|
265 |
-
hintStyle: const TextStyle(color:
|
266 |
border: OutlineInputBorder(
|
267 |
borderRadius: BorderRadius.circular(12),
|
268 |
borderSide: const BorderSide(
|
@@ -280,7 +280,7 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
280 |
focusedBorder: OutlineInputBorder(
|
281 |
borderRadius: BorderRadius.circular(12),
|
282 |
borderSide: const BorderSide(
|
283 |
-
color:
|
284 |
width: 1,
|
285 |
),
|
286 |
),
|
@@ -303,7 +303,7 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
303 |
child: CircularProgressIndicator(strokeWidth: 2),
|
304 |
)
|
305 |
: const Icon(Icons.reply),
|
306 |
-
color:
|
307 |
onPressed: _isSending ? null : _sendMessage,
|
308 |
),
|
309 |
],
|
@@ -381,7 +381,7 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
381 |
child: Column(
|
382 |
mainAxisSize: MainAxisSize.min,
|
383 |
children: [
|
384 |
-
Text(_error!, style: const TextStyle(color:
|
385 |
const SizedBox(height: 8),
|
386 |
ElevatedButton(
|
387 |
onPressed: () {
|
@@ -401,10 +401,10 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
401 |
return Container(
|
402 |
width: widget.isCompact ? double.infinity : 320,
|
403 |
decoration: BoxDecoration(
|
404 |
-
color:
|
405 |
borderRadius: BorderRadius.circular(12),
|
406 |
border: Border.all(
|
407 |
-
color:
|
408 |
width: 1,
|
409 |
),
|
410 |
),
|
@@ -414,12 +414,12 @@ class _ChatWidgetState extends State<ChatWidget> {
|
|
414 |
padding: EdgeInsets.all(16),
|
415 |
child: Row(
|
416 |
children: [
|
417 |
-
Icon(Icons.chat, color:
|
418 |
SizedBox(width: 8),
|
419 |
Text(
|
420 |
'Simulation log',
|
421 |
style: TextStyle(
|
422 |
-
color:
|
423 |
fontSize: 16,
|
424 |
fontWeight: FontWeight.bold,
|
425 |
),
|
|
|
156 |
child: Text(
|
157 |
'No messages yet',
|
158 |
style: TextStyle(
|
159 |
+
color: TikSlopColors.onSurfaceVariant,
|
160 |
fontSize: 14,
|
161 |
),
|
162 |
),
|
|
|
204 |
Text(
|
205 |
message.username,
|
206 |
style: const TextStyle(
|
207 |
+
color: TikSlopColors.onBackground,
|
208 |
fontWeight: FontWeight.bold,
|
209 |
),
|
210 |
),
|
|
|
212 |
Text(
|
213 |
_formatTime(message.timestamp),
|
214 |
style: const TextStyle(
|
215 |
+
color: TikSlopColors.onSurfaceVariant,
|
216 |
fontSize: 12,
|
217 |
),
|
218 |
),
|
|
|
221 |
const SizedBox(height: 4),
|
222 |
Text(
|
223 |
message.content,
|
224 |
+
style: const TextStyle(color: TikSlopColors.onSurface),
|
225 |
),
|
226 |
],
|
227 |
),
|
|
|
235 |
return Container(
|
236 |
padding: const EdgeInsets.all(8),
|
237 |
decoration: const BoxDecoration(
|
238 |
+
color: TikSlopColors.transparent,
|
239 |
border: Border(
|
240 |
top: BorderSide(
|
241 |
+
color: TikSlopColors.surfaceVariant,
|
242 |
width: 1,
|
243 |
),
|
244 |
),
|
|
|
248 |
Expanded(
|
249 |
child: TextField(
|
250 |
controller: _messageController,
|
251 |
+
style: const TextStyle(color: TikSlopColors.onSurface),
|
252 |
maxLength: 255,
|
253 |
maxLines: 1,
|
254 |
onChanged: (value) {
|
|
|
261 |
}
|
262 |
},
|
263 |
decoration: InputDecoration(
|
264 |
+
hintText: 'Chat with this tikslopr..',
|
265 |
+
hintStyle: const TextStyle(color: TikSlopColors.onSurfaceVariant, fontSize: 16),
|
266 |
border: OutlineInputBorder(
|
267 |
borderRadius: BorderRadius.circular(12),
|
268 |
borderSide: const BorderSide(
|
|
|
280 |
focusedBorder: OutlineInputBorder(
|
281 |
borderRadius: BorderRadius.circular(12),
|
282 |
borderSide: const BorderSide(
|
283 |
+
color: TikSlopColors.primary,
|
284 |
width: 1,
|
285 |
),
|
286 |
),
|
|
|
303 |
child: CircularProgressIndicator(strokeWidth: 2),
|
304 |
)
|
305 |
: const Icon(Icons.reply),
|
306 |
+
color: TikSlopColors.primary,
|
307 |
onPressed: _isSending ? null : _sendMessage,
|
308 |
),
|
309 |
],
|
|
|
381 |
child: Column(
|
382 |
mainAxisSize: MainAxisSize.min,
|
383 |
children: [
|
384 |
+
Text(_error!, style: const TextStyle(color: TikSlopColors.onBackground)),
|
385 |
const SizedBox(height: 8),
|
386 |
ElevatedButton(
|
387 |
onPressed: () {
|
|
|
401 |
return Container(
|
402 |
width: widget.isCompact ? double.infinity : 320,
|
403 |
decoration: BoxDecoration(
|
404 |
+
color: TikSlopColors.surface,
|
405 |
borderRadius: BorderRadius.circular(12),
|
406 |
border: Border.all(
|
407 |
+
color: TikSlopColors.surfaceVariant,
|
408 |
width: 1,
|
409 |
),
|
410 |
),
|
|
|
414 |
padding: EdgeInsets.all(16),
|
415 |
child: Row(
|
416 |
children: [
|
417 |
+
Icon(Icons.chat, color: TikSlopColors.onBackground),
|
418 |
SizedBox(width: 8),
|
419 |
Text(
|
420 |
'Simulation log',
|
421 |
style: TextStyle(
|
422 |
+
color: TikSlopColors.onBackground,
|
423 |
fontSize: 16,
|
424 |
fontWeight: FontWeight.bold,
|
425 |
),
|
lib/widgets/maintenance_screen.dart
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
// lib/widgets/maintenance_screen.dart
|
2 |
import 'package:flutter/material.dart';
|
3 |
-
import 'package:
|
4 |
|
5 |
class MaintenanceScreen extends StatelessWidget {
|
6 |
final Exception? error;
|
@@ -25,7 +25,7 @@ class MaintenanceScreen extends StatelessWidget {
|
|
25 |
),
|
26 |
const SizedBox(height: 24),
|
27 |
const Text(
|
28 |
-
'#
|
29 |
textAlign: TextAlign.center,
|
30 |
style: TextStyle(
|
31 |
color: Colors.grey,
|
|
|
1 |
// lib/widgets/maintenance_screen.dart
|
2 |
import 'package:flutter/material.dart';
|
3 |
+
import 'package:tikslop/theme/colors.dart';
|
4 |
|
5 |
class MaintenanceScreen extends StatelessWidget {
|
6 |
final Exception? error;
|
|
|
25 |
),
|
26 |
const SizedBox(height: 24),
|
27 |
const Text(
|
28 |
+
'#tikslop is currently in maintenance',
|
29 |
textAlign: TextAlign.center,
|
30 |
style: TextStyle(
|
31 |
color: Colors.grey,
|
lib/widgets/search_box.dart
CHANGED
@@ -59,7 +59,7 @@ class _SearchBoxState extends State<SearchBox> {
|
|
59 |
child: TextFormField(
|
60 |
controller: widget.controller,
|
61 |
focusNode: _focusNode,
|
62 |
-
style: const TextStyle(color:
|
63 |
enabled: widget.enabled,
|
64 |
textInputAction: TextInputAction.search,
|
65 |
onFieldSubmitted: _handleSubmitted,
|
@@ -68,9 +68,9 @@ class _SearchBoxState extends State<SearchBox> {
|
|
68 |
},
|
69 |
decoration: InputDecoration(
|
70 |
hintText: 'Explore the interdimensional TV! eg. "Elephants on Mars"',
|
71 |
-
hintStyle: const TextStyle(color:
|
72 |
filled: true,
|
73 |
-
fillColor:
|
74 |
border: OutlineInputBorder(
|
75 |
borderRadius: BorderRadius.circular(24),
|
76 |
borderSide: BorderSide.none,
|
@@ -91,7 +91,7 @@ class _SearchBoxState extends State<SearchBox> {
|
|
91 |
: IconButton(
|
92 |
icon: const Icon(
|
93 |
Icons.search,
|
94 |
-
color:
|
95 |
),
|
96 |
onPressed: () => _handleSubmitted(widget.controller.text),
|
97 |
),
|
|
|
59 |
child: TextFormField(
|
60 |
controller: widget.controller,
|
61 |
focusNode: _focusNode,
|
62 |
+
style: const TextStyle(color: TikSlopColors.onBackground),
|
63 |
enabled: widget.enabled,
|
64 |
textInputAction: TextInputAction.search,
|
65 |
onFieldSubmitted: _handleSubmitted,
|
|
|
68 |
},
|
69 |
decoration: InputDecoration(
|
70 |
hintText: 'Explore the interdimensional TV! eg. "Elephants on Mars"',
|
71 |
+
hintStyle: const TextStyle(color: TikSlopColors.onSurfaceVariant),
|
72 |
filled: true,
|
73 |
+
fillColor: TikSlopColors.surface,
|
74 |
border: OutlineInputBorder(
|
75 |
borderRadius: BorderRadius.circular(24),
|
76 |
borderSide: BorderSide.none,
|
|
|
91 |
: IconButton(
|
92 |
icon: const Icon(
|
93 |
Icons.search,
|
94 |
+
color: TikSlopColors.onSurfaceVariant,
|
95 |
),
|
96 |
onPressed: () => _handleSubmitted(widget.controller.text),
|
97 |
),
|
lib/widgets/video_card.dart
CHANGED
@@ -15,21 +15,21 @@ class VideoCard extends StatelessWidget {
|
|
15 |
Widget _buildThumbnail() {
|
16 |
if (video.thumbnailUrl.isEmpty) {
|
17 |
return Container(
|
18 |
-
color:
|
19 |
child: const Center(
|
20 |
child: Column(
|
21 |
mainAxisAlignment: MainAxisAlignment.center,
|
22 |
children: [
|
23 |
Icon(
|
24 |
Icons.movie_creation,
|
25 |
-
color:
|
26 |
size: 32,
|
27 |
),
|
28 |
SizedBox(height: 8),
|
29 |
Text(
|
30 |
'(TODO: thumbnails)',
|
31 |
style: TextStyle(
|
32 |
-
color:
|
33 |
fontSize: 12,
|
34 |
),
|
35 |
),
|
@@ -92,21 +92,21 @@ class VideoCard extends StatelessWidget {
|
|
92 |
|
93 |
Widget _buildErrorThumbnail() {
|
94 |
return Container(
|
95 |
-
color:
|
96 |
child: const Center(
|
97 |
child: Column(
|
98 |
mainAxisAlignment: MainAxisAlignment.center,
|
99 |
children: [
|
100 |
Icon(
|
101 |
Icons.broken_image,
|
102 |
-
color:
|
103 |
size: 32,
|
104 |
),
|
105 |
SizedBox(height: 8),
|
106 |
Text(
|
107 |
'Preview unavailable',
|
108 |
style: TextStyle(
|
109 |
-
color:
|
110 |
fontSize: 12,
|
111 |
),
|
112 |
),
|
@@ -151,7 +151,7 @@ class VideoCard extends StatelessWidget {
|
|
151 |
Text(
|
152 |
'LTX Video',
|
153 |
style: TextStyle(
|
154 |
-
color:
|
155 |
fontSize: 12,
|
156 |
),
|
157 |
),
|
@@ -165,16 +165,16 @@ class VideoCard extends StatelessWidget {
|
|
165 |
),
|
166 |
Container(
|
167 |
padding: const EdgeInsets.all(12),
|
168 |
-
color:
|
169 |
child: Row(
|
170 |
crossAxisAlignment: CrossAxisAlignment.start,
|
171 |
children: [
|
172 |
const CircleAvatar(
|
173 |
radius: 16,
|
174 |
-
backgroundColor:
|
175 |
child: Icon(
|
176 |
Icons.play_arrow,
|
177 |
-
color:
|
178 |
size: 20,
|
179 |
),
|
180 |
),
|
@@ -187,7 +187,7 @@ class VideoCard extends StatelessWidget {
|
|
187 |
Text(
|
188 |
video.title,
|
189 |
style: const TextStyle(
|
190 |
-
color:
|
191 |
fontWeight: FontWeight.w500,
|
192 |
fontSize: 14,
|
193 |
),
|
@@ -200,7 +200,7 @@ class VideoCard extends StatelessWidget {
|
|
200 |
child: Text(
|
201 |
video.description,
|
202 |
style: const TextStyle(
|
203 |
-
color:
|
204 |
fontSize: 12,
|
205 |
),
|
206 |
maxLines: 3,
|
|
|
15 |
Widget _buildThumbnail() {
|
16 |
if (video.thumbnailUrl.isEmpty) {
|
17 |
return Container(
|
18 |
+
color: TikSlopColors.surfaceVariant,
|
19 |
child: const Center(
|
20 |
child: Column(
|
21 |
mainAxisAlignment: MainAxisAlignment.center,
|
22 |
children: [
|
23 |
Icon(
|
24 |
Icons.movie_creation,
|
25 |
+
color: TikSlopColors.onSurfaceVariant,
|
26 |
size: 32,
|
27 |
),
|
28 |
SizedBox(height: 8),
|
29 |
Text(
|
30 |
'(TODO: thumbnails)',
|
31 |
style: TextStyle(
|
32 |
+
color: TikSlopColors.onSurfaceVariant,
|
33 |
fontSize: 12,
|
34 |
),
|
35 |
),
|
|
|
92 |
|
93 |
Widget _buildErrorThumbnail() {
|
94 |
return Container(
|
95 |
+
color: TikSlopColors.surfaceVariant,
|
96 |
child: const Center(
|
97 |
child: Column(
|
98 |
mainAxisAlignment: MainAxisAlignment.center,
|
99 |
children: [
|
100 |
Icon(
|
101 |
Icons.broken_image,
|
102 |
+
color: TikSlopColors.onSurfaceVariant,
|
103 |
size: 32,
|
104 |
),
|
105 |
SizedBox(height: 8),
|
106 |
Text(
|
107 |
'Preview unavailable',
|
108 |
style: TextStyle(
|
109 |
+
color: TikSlopColors.onSurfaceVariant,
|
110 |
fontSize: 12,
|
111 |
),
|
112 |
),
|
|
|
151 |
Text(
|
152 |
'LTX Video',
|
153 |
style: TextStyle(
|
154 |
+
color: TikSlopColors.onBackground,
|
155 |
fontSize: 12,
|
156 |
),
|
157 |
),
|
|
|
165 |
),
|
166 |
Container(
|
167 |
padding: const EdgeInsets.all(12),
|
168 |
+
color: TikSlopColors.surface,
|
169 |
child: Row(
|
170 |
crossAxisAlignment: CrossAxisAlignment.start,
|
171 |
children: [
|
172 |
const CircleAvatar(
|
173 |
radius: 16,
|
174 |
+
backgroundColor: TikSlopColors.surfaceVariant,
|
175 |
child: Icon(
|
176 |
Icons.play_arrow,
|
177 |
+
color: TikSlopColors.onSurfaceVariant,
|
178 |
size: 20,
|
179 |
),
|
180 |
),
|
|
|
187 |
Text(
|
188 |
video.title,
|
189 |
style: const TextStyle(
|
190 |
+
color: TikSlopColors.onBackground,
|
191 |
fontWeight: FontWeight.w500,
|
192 |
fontSize: 14,
|
193 |
),
|
|
|
200 |
child: Text(
|
201 |
video.description,
|
202 |
style: const TextStyle(
|
203 |
+
color: TikSlopColors.onSurfaceVariant,
|
204 |
fontSize: 12,
|
205 |
),
|
206 |
maxLines: 3,
|
lib/widgets/video_player/buffer_manager.dart
CHANGED
@@ -2,12 +2,12 @@
|
|
2 |
|
3 |
import 'dart:async';
|
4 |
import 'package:flutter/foundation.dart';
|
5 |
-
import 'package:
|
6 |
-
import 'package:
|
7 |
-
import 'package:
|
8 |
import 'package:video_player/video_player.dart';
|
9 |
-
import 'package:
|
10 |
-
import 'package:
|
11 |
|
12 |
/// Manages buffering and clip preloading for video player
|
13 |
class BufferManager {
|
|
|
2 |
|
3 |
import 'dart:async';
|
4 |
import 'package:flutter/foundation.dart';
|
5 |
+
import 'package:tikslop/config/config.dart';
|
6 |
+
import 'package:tikslop/services/clip_queue/video_clip.dart';
|
7 |
+
import 'package:tikslop/services/clip_queue/clip_queue_manager.dart';
|
8 |
import 'package:video_player/video_player.dart';
|
9 |
+
import 'package:tikslop/models/video_result.dart';
|
10 |
+
import 'package:tikslop/models/video_orientation.dart';
|
11 |
|
12 |
/// Manages buffering and clip preloading for video player
|
13 |
class BufferManager {
|
lib/widgets/video_player/nano_clip_manager.dart
CHANGED
@@ -3,11 +3,11 @@
|
|
3 |
import 'dart:async';
|
4 |
import 'dart:convert';
|
5 |
import 'package:flutter/foundation.dart';
|
6 |
-
import 'package:
|
7 |
-
import 'package:
|
8 |
-
import 'package:
|
9 |
-
import 'package:
|
10 |
-
import 'package:
|
11 |
import 'package:uuid/uuid.dart';
|
12 |
|
13 |
/// Manages a single video clip generation for thumbnail playback
|
|
|
3 |
import 'dart:async';
|
4 |
import 'dart:convert';
|
5 |
import 'package:flutter/foundation.dart';
|
6 |
+
import 'package:tikslop/models/video_result.dart';
|
7 |
+
import 'package:tikslop/services/clip_queue/video_clip.dart';
|
8 |
+
import 'package:tikslop/services/clip_queue/clip_states.dart';
|
9 |
+
import 'package:tikslop/services/websocket_api_service.dart';
|
10 |
+
import 'package:tikslop/utils/seed.dart';
|
11 |
import 'package:uuid/uuid.dart';
|
12 |
|
13 |
/// Manages a single video clip generation for thumbnail playback
|
lib/widgets/video_player/nano_video_player.dart
CHANGED
@@ -4,11 +4,11 @@ import 'dart:async';
|
|
4 |
import 'package:flutter/material.dart';
|
5 |
import 'package:flutter/foundation.dart' show kIsWeb;
|
6 |
import 'package:video_player/video_player.dart';
|
7 |
-
import 'package:
|
8 |
-
import 'package:
|
9 |
-
import 'package:
|
10 |
-
import 'package:
|
11 |
-
import 'package:
|
12 |
|
13 |
// Conditionally import dart:html for web platform
|
14 |
import '../web_utils.dart' if (dart.library.html) 'dart:html' as html;
|
@@ -247,7 +247,7 @@ class _NanoVideoPlayerState extends State<NanoVideoPlayer> with WidgetsBindingOb
|
|
247 |
children: [
|
248 |
// Base layer: Video or placeholder
|
249 |
Container(
|
250 |
-
color:
|
251 |
child: _controller?.value.isInitialized == true
|
252 |
? AspectRatio(
|
253 |
aspectRatio: _controller!.value.aspectRatio,
|
|
|
4 |
import 'package:flutter/material.dart';
|
5 |
import 'package:flutter/foundation.dart' show kIsWeb;
|
6 |
import 'package:video_player/video_player.dart';
|
7 |
+
import 'package:tikslop/models/video_result.dart';
|
8 |
+
import 'package:tikslop/theme/colors.dart';
|
9 |
+
import 'package:tikslop/widgets/video_player/nano_clip_manager.dart';
|
10 |
+
import 'package:tikslop/widgets/video_player/lifecycle_manager.dart';
|
11 |
+
import 'package:tikslop/widgets/ai_content_disclaimer.dart';
|
12 |
|
13 |
// Conditionally import dart:html for web platform
|
14 |
import '../web_utils.dart' if (dart.library.html) 'dart:html' as html;
|
|
|
247 |
children: [
|
248 |
// Base layer: Video or placeholder
|
249 |
Container(
|
250 |
+
color: TikSlopColors.surfaceVariant,
|
251 |
child: _controller?.value.isInitialized == true
|
252 |
? AspectRatio(
|
253 |
aspectRatio: _controller!.value.aspectRatio,
|
lib/widgets/video_player/playback_controller.dart
CHANGED
@@ -4,8 +4,8 @@ import 'dart:async';
|
|
4 |
import 'package:flutter/foundation.dart';
|
5 |
import 'package:flutter/material.dart';
|
6 |
import 'package:video_player/video_player.dart';
|
7 |
-
import 'package:
|
8 |
-
import 'package:
|
9 |
|
10 |
/// Manages video playback logic for the video player
|
11 |
class PlaybackController {
|
|
|
4 |
import 'package:flutter/foundation.dart';
|
5 |
import 'package:flutter/material.dart';
|
6 |
import 'package:video_player/video_player.dart';
|
7 |
+
import 'package:tikslop/config/config.dart';
|
8 |
+
import 'package:tikslop/services/clip_queue/video_clip.dart';
|
9 |
|
10 |
/// Manages video playback logic for the video player
|
11 |
class PlaybackController {
|
lib/widgets/video_player/ui_components.dart
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
// lib/widgets/video_player/ui_components.dart
|
2 |
|
3 |
import 'package:flutter/material.dart';
|
4 |
-
import 'package:
|
5 |
-
import 'package:
|
6 |
-
import 'package:
|
7 |
|
8 |
/// Builds a placeholder widget for when video is not loaded
|
9 |
Widget buildPlaceholder(String? initialThumbnailUrl) {
|
@@ -45,7 +45,7 @@ Widget buildErrorPlaceholder() {
|
|
45 |
child: Icon(
|
46 |
Icons.broken_image,
|
47 |
size: 64,
|
48 |
-
color:
|
49 |
),
|
50 |
);
|
51 |
}
|
|
|
1 |
// lib/widgets/video_player/ui_components.dart
|
2 |
|
3 |
import 'package:flutter/material.dart';
|
4 |
+
import 'package:tikslop/theme/colors.dart';
|
5 |
+
import 'package:tikslop/widgets/ai_content_disclaimer.dart';
|
6 |
+
import 'package:tikslop/config/config.dart';
|
7 |
|
8 |
/// Builds a placeholder widget for when video is not loaded
|
9 |
Widget buildPlaceholder(String? initialThumbnailUrl) {
|
|
|
45 |
child: Icon(
|
46 |
Icons.broken_image,
|
47 |
size: 64,
|
48 |
+
color: TikSlopColors.onSurfaceVariant,
|
49 |
),
|
50 |
);
|
51 |
}
|
lib/widgets/video_player/video_player_widget.dart
CHANGED
@@ -3,14 +3,14 @@
|
|
3 |
import 'dart:async';
|
4 |
import 'dart:math' as math;
|
5 |
import 'dart:io' show Platform;
|
6 |
-
import 'package:
|
7 |
import 'package:flutter/material.dart';
|
8 |
import 'package:flutter/foundation.dart' show kDebugMode, kIsWeb;
|
9 |
import 'package:flutter/foundation.dart';
|
10 |
import 'package:video_player/video_player.dart';
|
11 |
-
import 'package:
|
12 |
-
import 'package:
|
13 |
-
import 'package:
|
14 |
|
15 |
// Import components
|
16 |
import 'playback_controller.dart';
|
@@ -557,7 +557,7 @@ class _VideoPlayerWidgetState extends State<VideoPlayerWidget> with WidgetsBindi
|
|
557 |
ClipRRect(
|
558 |
borderRadius: BorderRadius.circular(widget.borderRadius),
|
559 |
child: Container(
|
560 |
-
color:
|
561 |
child: controller?.value.isInitialized ?? false
|
562 |
? VideoPlayer(controller!)
|
563 |
: ui.buildPlaceholder(widget.initialThumbnailUrl),
|
|
|
3 |
import 'dart:async';
|
4 |
import 'dart:math' as math;
|
5 |
import 'dart:io' show Platform;
|
6 |
+
import 'package:tikslop/config/config.dart';
|
7 |
import 'package:flutter/material.dart';
|
8 |
import 'package:flutter/foundation.dart' show kDebugMode, kIsWeb;
|
9 |
import 'package:flutter/foundation.dart';
|
10 |
import 'package:video_player/video_player.dart';
|
11 |
+
import 'package:tikslop/models/video_result.dart';
|
12 |
+
import 'package:tikslop/models/video_orientation.dart';
|
13 |
+
import 'package:tikslop/theme/colors.dart';
|
14 |
|
15 |
// Import components
|
16 |
import 'playback_controller.dart';
|
|
|
557 |
ClipRRect(
|
558 |
borderRadius: BorderRadius.circular(widget.borderRadius),
|
559 |
child: Container(
|
560 |
+
color: TikSlopColors.surfaceVariant,
|
561 |
child: controller?.value.isInitialized ?? false
|
562 |
? VideoPlayer(controller!)
|
563 |
: ui.buildPlaceholder(widget.initialThumbnailUrl),
|
lib/widgets/web_utils.dart
CHANGED
@@ -2,7 +2,7 @@ import 'dart:async';
|
|
2 |
import 'package:flutter/foundation.dart';
|
3 |
|
4 |
// Platform-specific imports handling
|
5 |
-
import 'package:universal_html/html.dart' if (dart.library.io) 'package:
|
6 |
|
7 |
/// Get URL parameters from the current URL (web only)
|
8 |
Map<String, String> getUrlParameters() {
|
|
|
2 |
import 'package:flutter/foundation.dart';
|
3 |
|
4 |
// Platform-specific imports handling
|
5 |
+
import 'package:universal_html/html.dart' if (dart.library.io) 'package:tikslop/services/html_stub.dart' as html;
|
6 |
|
7 |
/// Get URL parameters from the current URL (web only)
|
8 |
Map<String, String> getUrlParameters() {
|
linux/CMakeLists.txt
CHANGED
@@ -4,10 +4,10 @@ project(runner LANGUAGES CXX)
|
|
4 |
|
5 |
# The name of the executable created for the application. Change this to change
|
6 |
# the on-disk name of your application.
|
7 |
-
set(BINARY_NAME "
|
8 |
# The unique GTK application identifier for this application. See:
|
9 |
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
10 |
-
set(APPLICATION_ID "com.example.
|
11 |
|
12 |
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
13 |
# versions of CMake.
|
|
|
4 |
|
5 |
# The name of the executable created for the application. Change this to change
|
6 |
# the on-disk name of your application.
|
7 |
+
set(BINARY_NAME "tikslop")
|
8 |
# The unique GTK application identifier for this application. See:
|
9 |
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
10 |
+
set(APPLICATION_ID "com.example.tikslop")
|
11 |
|
12 |
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
13 |
# versions of CMake.
|
linux/my_application.cc
CHANGED
@@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) {
|
|
40 |
if (use_header_bar) {
|
41 |
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
42 |
gtk_widget_show(GTK_WIDGET(header_bar));
|
43 |
-
gtk_header_bar_set_title(header_bar, "
|
44 |
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
45 |
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
46 |
} else {
|
47 |
-
gtk_window_set_title(window, "
|
48 |
}
|
49 |
|
50 |
gtk_window_set_default_size(window, 1280, 720);
|
|
|
40 |
if (use_header_bar) {
|
41 |
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
42 |
gtk_widget_show(GTK_WIDGET(header_bar));
|
43 |
+
gtk_header_bar_set_title(header_bar, "tikslop");
|
44 |
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
45 |
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
46 |
} else {
|
47 |
+
gtk_window_set_title(window, "tikslop");
|
48 |
}
|
49 |
|
50 |
gtk_window_set_default_size(window, 1280, 720);
|