Julian Bilcke
		
	commited on
		
		
					Commit 
							
							·
						
						3b81d2d
	
1
								Parent(s):
							
							9ee3c2f
								
update for oauth
Browse files- .env +2 -0
 - package-lock.json +34 -34
 - package.json +1 -1
 - src/app/interface/bottom-bar/bottom-bar.tsx +1 -1
 - src/app/interface/grid/index.tsx +1 -1
 - src/app/interface/page/index.tsx +5 -3
 - src/app/interface/panel/bubble/index.tsx +2 -2
 - src/app/interface/panel/index.tsx +13 -11
 - src/app/interface/select-global-layout/index.tsx +39 -0
 - src/app/interface/select-layout/index.tsx +56 -0
 - src/app/interface/share/index.tsx +3 -3
 - src/app/interface/top-menu/index.tsx +19 -57
 - src/app/layouts/index.tsx +15 -0
 - src/app/layouts/settings.tsx +52 -0
 - src/app/main.tsx +5 -2
 - src/app/queries/getDynamicConfig.ts +3 -0
 - src/app/store/index.ts +26 -9
 - src/lib/getImageDimension.ts +12 -2
 - src/lib/getOAuthRedirectUrl.ts +11 -0
 - src/lib/isValidNumber.ts +3 -0
 - src/lib/parseLayoutFromStoryboards.ts +38 -0
 - src/lib/parsePresetFromPrompts.ts +37 -0
 - src/lib/useImageDimension.ts +2 -0
 - src/lib/useIsBusy.ts +9 -0
 - src/lib/useOAuth.ts +5 -1
 
    	
        .env
    CHANGED
    
    | 
         @@ -24,6 +24,8 @@ NEXT_PUBLIC_ENABLE_RATE_LIMITER="false" 
     | 
|
| 24 | 
         
             
            ENABLE_HUGGING_FACE_OAUTH=
         
     | 
| 25 | 
         
             
            ENABLE_HUGGING_FACE_OAUTH_WALL=
         
     | 
| 26 | 
         
             
            HUGGING_FACE_OAUTH_CLIENT_ID=
         
     | 
| 
         | 
|
| 
         | 
|
| 27 | 
         
             
            HUGGING_FACE_OAUTH_REDIRECT_URL=
         
     | 
| 28 | 
         | 
| 29 | 
         
             
            # this one must be kept secret (and is unused for now)
         
     | 
| 
         | 
|
| 24 | 
         
             
            ENABLE_HUGGING_FACE_OAUTH=
         
     | 
| 25 | 
         
             
            ENABLE_HUGGING_FACE_OAUTH_WALL=
         
     | 
| 26 | 
         
             
            HUGGING_FACE_OAUTH_CLIENT_ID=
         
     | 
| 27 | 
         
            +
             
     | 
| 28 | 
         
            +
            # in production this should be the space's domain and/or URL
         
     | 
| 29 | 
         
             
            HUGGING_FACE_OAUTH_REDIRECT_URL=
         
     | 
| 30 | 
         | 
| 31 | 
         
             
            # this one must be kept secret (and is unused for now)
         
     | 
    	
        package-lock.json
    CHANGED
    
    | 
         @@ -8,7 +8,7 @@ 
     | 
|
| 8 | 
         
             
                  "name": "@jbilcke/comic-factory",
         
     | 
| 9 | 
         
             
                  "version": "1.2.0",
         
     | 
| 10 | 
         
             
                  "dependencies": {
         
     | 
| 11 | 
         
            -
                    "@aitube/clap": "0.0. 
     | 
| 12 | 
         
             
                    "@anthropic-ai/sdk": "^0.19.1",
         
     | 
| 13 | 
         
             
                    "@huggingface/hub": "^0.14.2",
         
     | 
| 14 | 
         
             
                    "@huggingface/inference": "^2.6.1",
         
     | 
| 
         @@ -81,9 +81,9 @@ 
     | 
|
| 81 | 
         
             
                  }
         
     | 
| 82 | 
         
             
                },
         
     | 
| 83 | 
         
             
                "node_modules/@aitube/clap": {
         
     | 
| 84 | 
         
            -
                  "version": "0.0. 
     | 
| 85 | 
         
            -
                  "resolved": "https://registry.npmjs.org/@aitube/clap/-/clap-0.0. 
     | 
| 86 | 
         
            -
                  "integrity": "sha512- 
     | 
| 87 | 
         
             
                  "dependencies": {
         
     | 
| 88 | 
         
             
                    "pure-uuid": "^1.8.1",
         
     | 
| 89 | 
         
             
                    "yaml": "^2.4.1"
         
     | 
| 
         @@ -119,9 +119,9 @@ 
     | 
|
| 119 | 
         
             
                  }
         
     | 
| 120 | 
         
             
                },
         
     | 
| 121 | 
         
             
                "node_modules/@anthropic-ai/sdk/node_modules/@types/node": {
         
     | 
| 122 | 
         
            -
                  "version": "18.19. 
     | 
| 123 | 
         
            -
                  "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19. 
     | 
| 124 | 
         
            -
                  "integrity": "sha512- 
     | 
| 125 | 
         
             
                  "dependencies": {
         
     | 
| 126 | 
         
             
                    "undici-types": "~5.26.4"
         
     | 
| 127 | 
         
             
                  }
         
     | 
| 
         @@ -227,9 +227,9 @@ 
     | 
|
| 227 | 
         
             
                  }
         
     | 
| 228 | 
         
             
                },
         
     | 
| 229 | 
         
             
                "node_modules/@floating-ui/dom": {
         
     | 
| 230 | 
         
            -
                  "version": "1.6. 
     | 
| 231 | 
         
            -
                  "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6. 
     | 
| 232 | 
         
            -
                  "integrity": "sha512- 
     | 
| 233 | 
         
             
                  "dependencies": {
         
     | 
| 234 | 
         
             
                    "@floating-ui/core": "^1.0.0",
         
     | 
| 235 | 
         
             
                    "@floating-ui/utils": "^0.2.0"
         
     | 
| 
         @@ -2890,9 +2890,9 @@ 
     | 
|
| 2890 | 
         
             
                  }
         
     | 
| 2891 | 
         
             
                },
         
     | 
| 2892 | 
         
             
                "node_modules/caniuse-lite": {
         
     | 
| 2893 | 
         
            -
                  "version": "1.0. 
     | 
| 2894 | 
         
            -
                  "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0. 
     | 
| 2895 | 
         
            -
                  "integrity": "sha512- 
     | 
| 2896 | 
         
             
                  "funding": [
         
     | 
| 2897 | 
         
             
                    {
         
     | 
| 2898 | 
         
             
                      "type": "opencollective",
         
     | 
| 
         @@ -3322,9 +3322,9 @@ 
     | 
|
| 3322 | 
         
             
                  }
         
     | 
| 3323 | 
         
             
                },
         
     | 
| 3324 | 
         
             
                "node_modules/cookies-next/node_modules/@types/node": {
         
     | 
| 3325 | 
         
            -
                  "version": "16.18. 
     | 
| 3326 | 
         
            -
                  "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18. 
     | 
| 3327 | 
         
            -
                  "integrity": "sha512- 
     | 
| 3328 | 
         
             
                },
         
     | 
| 3329 | 
         
             
                "node_modules/create-require": {
         
     | 
| 3330 | 
         
             
                  "version": "1.1.1",
         
     | 
| 
         @@ -3648,9 +3648,9 @@ 
     | 
|
| 3648 | 
         
             
                  "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
         
     | 
| 3649 | 
         
             
                },
         
     | 
| 3650 | 
         
             
                "node_modules/electron-to-chromium": {
         
     | 
| 3651 | 
         
            -
                  "version": "1.4. 
     | 
| 3652 | 
         
            -
                  "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4. 
     | 
| 3653 | 
         
            -
                  "integrity": "sha512- 
     | 
| 3654 | 
         
             
                },
         
     | 
| 3655 | 
         
             
                "node_modules/emoji-regex": {
         
     | 
| 3656 | 
         
             
                  "version": "9.2.2",
         
     | 
| 
         @@ -3666,9 +3666,9 @@ 
     | 
|
| 3666 | 
         
             
                  }
         
     | 
| 3667 | 
         
             
                },
         
     | 
| 3668 | 
         
             
                "node_modules/enhanced-resolve": {
         
     | 
| 3669 | 
         
            -
                  "version": "5.16. 
     | 
| 3670 | 
         
            -
                  "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16. 
     | 
| 3671 | 
         
            -
                  "integrity": "sha512- 
     | 
| 3672 | 
         
             
                  "dependencies": {
         
     | 
| 3673 | 
         
             
                    "graceful-fs": "^4.2.4",
         
     | 
| 3674 | 
         
             
                    "tapable": "^2.2.0"
         
     | 
| 
         @@ -4554,9 +4554,9 @@ 
     | 
|
| 4554 | 
         
             
                  }
         
     | 
| 4555 | 
         
             
                },
         
     | 
| 4556 | 
         
             
                "node_modules/get-tsconfig": {
         
     | 
| 4557 | 
         
            -
                  "version": "4.7. 
     | 
| 4558 | 
         
            -
                  "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7. 
     | 
| 4559 | 
         
            -
                  "integrity": "sha512- 
     | 
| 4560 | 
         
             
                  "dependencies": {
         
     | 
| 4561 | 
         
             
                    "resolve-pkg-maps": "^1.0.0"
         
     | 
| 4562 | 
         
             
                  },
         
     | 
| 
         @@ -4680,9 +4680,9 @@ 
     | 
|
| 4680 | 
         
             
                  }
         
     | 
| 4681 | 
         
             
                },
         
     | 
| 4682 | 
         
             
                "node_modules/groq-sdk/node_modules/@types/node": {
         
     | 
| 4683 | 
         
            -
                  "version": "18.19. 
     | 
| 4684 | 
         
            -
                  "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19. 
     | 
| 4685 | 
         
            -
                  "integrity": "sha512- 
     | 
| 4686 | 
         
             
                  "dependencies": {
         
     | 
| 4687 | 
         
             
                    "undici-types": "~5.26.4"
         
     | 
| 4688 | 
         
             
                  }
         
     | 
| 
         @@ -8298,9 +8298,9 @@ 
     | 
|
| 8298 | 
         
             
                  }
         
     | 
| 8299 | 
         
             
                },
         
     | 
| 8300 | 
         
             
                "node_modules/openai": {
         
     | 
| 8301 | 
         
            -
                  "version": "4. 
     | 
| 8302 | 
         
            -
                  "resolved": "https://registry.npmjs.org/openai/-/openai-4. 
     | 
| 8303 | 
         
            -
                  "integrity": "sha512- 
     | 
| 8304 | 
         
             
                  "dependencies": {
         
     | 
| 8305 | 
         
             
                    "@types/node": "^18.11.18",
         
     | 
| 8306 | 
         
             
                    "@types/node-fetch": "^2.6.4",
         
     | 
| 
         @@ -8316,9 +8316,9 @@ 
     | 
|
| 8316 | 
         
             
                  }
         
     | 
| 8317 | 
         
             
                },
         
     | 
| 8318 | 
         
             
                "node_modules/openai/node_modules/@types/node": {
         
     | 
| 8319 | 
         
            -
                  "version": "18.19. 
     | 
| 8320 | 
         
            -
                  "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19. 
     | 
| 8321 | 
         
            -
                  "integrity": "sha512- 
     | 
| 8322 | 
         
             
                  "dependencies": {
         
     | 
| 8323 | 
         
             
                    "undici-types": "~5.26.4"
         
     | 
| 8324 | 
         
             
                  }
         
     | 
| 
         | 
|
| 8 | 
         
             
                  "name": "@jbilcke/comic-factory",
         
     | 
| 9 | 
         
             
                  "version": "1.2.0",
         
     | 
| 10 | 
         
             
                  "dependencies": {
         
     | 
| 11 | 
         
            +
                    "@aitube/clap": "0.0.14",
         
     | 
| 12 | 
         
             
                    "@anthropic-ai/sdk": "^0.19.1",
         
     | 
| 13 | 
         
             
                    "@huggingface/hub": "^0.14.2",
         
     | 
| 14 | 
         
             
                    "@huggingface/inference": "^2.6.1",
         
     | 
| 
         | 
|
| 81 | 
         
             
                  }
         
     | 
| 82 | 
         
             
                },
         
     | 
| 83 | 
         
             
                "node_modules/@aitube/clap": {
         
     | 
| 84 | 
         
            +
                  "version": "0.0.14",
         
     | 
| 85 | 
         
            +
                  "resolved": "https://registry.npmjs.org/@aitube/clap/-/clap-0.0.14.tgz",
         
     | 
| 86 | 
         
            +
                  "integrity": "sha512-i4mq3YFecWVOTS/p5QaSQ0VJfurKXlyRc8FJMqKI6P/7rpf4vE4IL+jBKa4HPsYeNt85/KOt3MJKEFVtgiWGfQ==",
         
     | 
| 87 | 
         
             
                  "dependencies": {
         
     | 
| 88 | 
         
             
                    "pure-uuid": "^1.8.1",
         
     | 
| 89 | 
         
             
                    "yaml": "^2.4.1"
         
     | 
| 
         | 
|
| 119 | 
         
             
                  }
         
     | 
| 120 | 
         
             
                },
         
     | 
| 121 | 
         
             
                "node_modules/@anthropic-ai/sdk/node_modules/@types/node": {
         
     | 
| 122 | 
         
            +
                  "version": "18.19.32",
         
     | 
| 123 | 
         
            +
                  "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.32.tgz",
         
     | 
| 124 | 
         
            +
                  "integrity": "sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==",
         
     | 
| 125 | 
         
             
                  "dependencies": {
         
     | 
| 126 | 
         
             
                    "undici-types": "~5.26.4"
         
     | 
| 127 | 
         
             
                  }
         
     | 
| 
         | 
|
| 227 | 
         
             
                  }
         
     | 
| 228 | 
         
             
                },
         
     | 
| 229 | 
         
             
                "node_modules/@floating-ui/dom": {
         
     | 
| 230 | 
         
            +
                  "version": "1.6.5",
         
     | 
| 231 | 
         
            +
                  "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
         
     | 
| 232 | 
         
            +
                  "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
         
     | 
| 233 | 
         
             
                  "dependencies": {
         
     | 
| 234 | 
         
             
                    "@floating-ui/core": "^1.0.0",
         
     | 
| 235 | 
         
             
                    "@floating-ui/utils": "^0.2.0"
         
     | 
| 
         | 
|
| 2890 | 
         
             
                  }
         
     | 
| 2891 | 
         
             
                },
         
     | 
| 2892 | 
         
             
                "node_modules/caniuse-lite": {
         
     | 
| 2893 | 
         
            +
                  "version": "1.0.30001616",
         
     | 
| 2894 | 
         
            +
                  "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz",
         
     | 
| 2895 | 
         
            +
                  "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==",
         
     | 
| 2896 | 
         
             
                  "funding": [
         
     | 
| 2897 | 
         
             
                    {
         
     | 
| 2898 | 
         
             
                      "type": "opencollective",
         
     | 
| 
         | 
|
| 3322 | 
         
             
                  }
         
     | 
| 3323 | 
         
             
                },
         
     | 
| 3324 | 
         
             
                "node_modules/cookies-next/node_modules/@types/node": {
         
     | 
| 3325 | 
         
            +
                  "version": "16.18.97",
         
     | 
| 3326 | 
         
            +
                  "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.97.tgz",
         
     | 
| 3327 | 
         
            +
                  "integrity": "sha512-4muilE1Lbfn57unR+/nT9AFjWk0MtWi5muwCEJqnOvfRQDbSfLCUdN7vCIg8TYuaANfhLOV85ve+FNpiUsbSRg=="
         
     | 
| 3328 | 
         
             
                },
         
     | 
| 3329 | 
         
             
                "node_modules/create-require": {
         
     | 
| 3330 | 
         
             
                  "version": "1.1.1",
         
     | 
| 
         | 
|
| 3648 | 
         
             
                  "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
         
     | 
| 3649 | 
         
             
                },
         
     | 
| 3650 | 
         
             
                "node_modules/electron-to-chromium": {
         
     | 
| 3651 | 
         
            +
                  "version": "1.4.757",
         
     | 
| 3652 | 
         
            +
                  "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.757.tgz",
         
     | 
| 3653 | 
         
            +
                  "integrity": "sha512-jftDaCknYSSt/+KKeXzH3LX5E2CvRLm75P3Hj+J/dv3CL0qUYcOt13d5FN1NiL5IJbbhzHrb3BomeG2tkSlZmw=="
         
     | 
| 3654 | 
         
             
                },
         
     | 
| 3655 | 
         
             
                "node_modules/emoji-regex": {
         
     | 
| 3656 | 
         
             
                  "version": "9.2.2",
         
     | 
| 
         | 
|
| 3666 | 
         
             
                  }
         
     | 
| 3667 | 
         
             
                },
         
     | 
| 3668 | 
         
             
                "node_modules/enhanced-resolve": {
         
     | 
| 3669 | 
         
            +
                  "version": "5.16.1",
         
     | 
| 3670 | 
         
            +
                  "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz",
         
     | 
| 3671 | 
         
            +
                  "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==",
         
     | 
| 3672 | 
         
             
                  "dependencies": {
         
     | 
| 3673 | 
         
             
                    "graceful-fs": "^4.2.4",
         
     | 
| 3674 | 
         
             
                    "tapable": "^2.2.0"
         
     | 
| 
         | 
|
| 4554 | 
         
             
                  }
         
     | 
| 4555 | 
         
             
                },
         
     | 
| 4556 | 
         
             
                "node_modules/get-tsconfig": {
         
     | 
| 4557 | 
         
            +
                  "version": "4.7.4",
         
     | 
| 4558 | 
         
            +
                  "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.4.tgz",
         
     | 
| 4559 | 
         
            +
                  "integrity": "sha512-ofbkKj+0pjXjhejr007J/fLf+sW+8H7K5GCm+msC8q3IpvgjobpyPqSRFemNyIMxklC0zeJpi7VDFna19FacvQ==",
         
     | 
| 4560 | 
         
             
                  "dependencies": {
         
     | 
| 4561 | 
         
             
                    "resolve-pkg-maps": "^1.0.0"
         
     | 
| 4562 | 
         
             
                  },
         
     | 
| 
         | 
|
| 4680 | 
         
             
                  }
         
     | 
| 4681 | 
         
             
                },
         
     | 
| 4682 | 
         
             
                "node_modules/groq-sdk/node_modules/@types/node": {
         
     | 
| 4683 | 
         
            +
                  "version": "18.19.32",
         
     | 
| 4684 | 
         
            +
                  "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.32.tgz",
         
     | 
| 4685 | 
         
            +
                  "integrity": "sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==",
         
     | 
| 4686 | 
         
             
                  "dependencies": {
         
     | 
| 4687 | 
         
             
                    "undici-types": "~5.26.4"
         
     | 
| 4688 | 
         
             
                  }
         
     | 
| 
         | 
|
| 8298 | 
         
             
                  }
         
     | 
| 8299 | 
         
             
                },
         
     | 
| 8300 | 
         
             
                "node_modules/openai": {
         
     | 
| 8301 | 
         
            +
                  "version": "4.42.0",
         
     | 
| 8302 | 
         
            +
                  "resolved": "https://registry.npmjs.org/openai/-/openai-4.42.0.tgz",
         
     | 
| 8303 | 
         
            +
                  "integrity": "sha512-xbiQQ2YNqdkE6cHqeWKa7lsAvdYfgp84XiNFOVkAMa6+9KpmOL4hCWCRR6e6I/clpaens/T9XeLVtyC5StXoRw==",
         
     | 
| 8304 | 
         
             
                  "dependencies": {
         
     | 
| 8305 | 
         
             
                    "@types/node": "^18.11.18",
         
     | 
| 8306 | 
         
             
                    "@types/node-fetch": "^2.6.4",
         
     | 
| 
         | 
|
| 8316 | 
         
             
                  }
         
     | 
| 8317 | 
         
             
                },
         
     | 
| 8318 | 
         
             
                "node_modules/openai/node_modules/@types/node": {
         
     | 
| 8319 | 
         
            +
                  "version": "18.19.32",
         
     | 
| 8320 | 
         
            +
                  "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.32.tgz",
         
     | 
| 8321 | 
         
            +
                  "integrity": "sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==",
         
     | 
| 8322 | 
         
             
                  "dependencies": {
         
     | 
| 8323 | 
         
             
                    "undici-types": "~5.26.4"
         
     | 
| 8324 | 
         
             
                  }
         
     | 
    	
        package.json
    CHANGED
    
    | 
         @@ -9,7 +9,7 @@ 
     | 
|
| 9 | 
         
             
                "lint": "next lint"
         
     | 
| 10 | 
         
             
              },
         
     | 
| 11 | 
         
             
              "dependencies": {
         
     | 
| 12 | 
         
            -
                "@aitube/clap": "0.0. 
     | 
| 13 | 
         
             
                "@anthropic-ai/sdk": "^0.19.1",
         
     | 
| 14 | 
         
             
                "@huggingface/hub": "^0.14.2",
         
     | 
| 15 | 
         
             
                "@huggingface/inference": "^2.6.1",
         
     | 
| 
         | 
|
| 9 | 
         
             
                "lint": "next lint"
         
     | 
| 10 | 
         
             
              },
         
     | 
| 11 | 
         
             
              "dependencies": {
         
     | 
| 12 | 
         
            +
                "@aitube/clap": "0.0.14",
         
     | 
| 13 | 
         
             
                "@anthropic-ai/sdk": "^0.19.1",
         
     | 
| 14 | 
         
             
                "@huggingface/hub": "^0.14.2",
         
     | 
| 15 | 
         
             
                "@huggingface/inference": "^2.6.1",
         
     | 
    	
        src/app/interface/bottom-bar/bottom-bar.tsx
    CHANGED
    
    | 
         @@ -29,7 +29,7 @@ function BottomBar() { 
     | 
|
| 29 | 
         | 
| 30 | 
         
             
              const preset = useStore(s => s.preset)
         
     | 
| 31 | 
         | 
| 32 | 
         
            -
              const canSeeBetaFeatures = getParam<boolean>("beta", false)
         
     | 
| 33 | 
         | 
| 34 | 
         
             
              const allStatus = Object.values(panelGenerationStatus)
         
     | 
| 35 | 
         
             
              const remainingImages = allStatus.reduce((acc, s) => (acc + (s ? 1 : 0)), 0)
         
     | 
| 
         | 
|
| 29 | 
         | 
| 30 | 
         
             
              const preset = useStore(s => s.preset)
         
     | 
| 31 | 
         | 
| 32 | 
         
            +
              const canSeeBetaFeatures = true // getParam<boolean>("beta", false)
         
     | 
| 33 | 
         | 
| 34 | 
         
             
              const allStatus = Object.values(panelGenerationStatus)
         
     | 
| 35 | 
         
             
              const remainingImages = allStatus.reduce((acc, s) => (acc + (s ? 1 : 0)), 0)
         
     | 
    	
        src/app/interface/grid/index.tsx
    CHANGED
    
    | 
         @@ -6,7 +6,7 @@ import { cn } from "@/lib/utils" 
     | 
|
| 6 | 
         
             
            import { useStore } from "@/app/store"
         
     | 
| 7 | 
         | 
| 8 | 
         
             
            export function Grid({ children, className }: { children: ReactNode; className: string }) {
         
     | 
| 9 | 
         
            -
              const zoomLevel = useStore( 
     | 
| 10 | 
         | 
| 11 | 
         
             
              return (
         
     | 
| 12 | 
         
             
                <div
         
     | 
| 
         | 
|
| 6 | 
         
             
            import { useStore } from "@/app/store"
         
     | 
| 7 | 
         | 
| 8 | 
         
             
            export function Grid({ children, className }: { children: ReactNode; className: string }) {
         
     | 
| 9 | 
         
            +
              const zoomLevel = useStore(s => s.zoomLevel)
         
     | 
| 10 | 
         | 
| 11 | 
         
             
              return (
         
     | 
| 12 | 
         
             
                <div
         
     | 
    	
        src/app/interface/page/index.tsx
    CHANGED
    
    | 
         @@ -7,8 +7,8 @@ import { useStore } from "@/app/store" 
     | 
|
| 7 | 
         
             
            import { cn } from "@/lib/utils"
         
     | 
| 8 | 
         | 
| 9 | 
         
             
            export function Page({ page }: { page: number }) {
         
     | 
| 10 | 
         
            -
              const zoomLevel = useStore( 
     | 
| 11 | 
         
            -
              const layouts = useStore( 
     | 
| 12 | 
         | 
| 13 | 
         
             
              // attention: here we use a fallback to layouts[0]
         
     | 
| 14 | 
         
             
              // if no predetermined layout exists for this page number
         
     | 
| 
         @@ -39,9 +39,11 @@ export function Page({ page }: { page: number }) { 
     | 
|
| 39 | 
         
             
              // this was used to keep track of the page HTML element,
         
     | 
| 40 | 
         
             
              // for use with a HTML-to-bitmap library
         
     | 
| 41 | 
         
             
              // but the CSS layout wasn't followed properly and it depended on the zoom level
         
     | 
| 
         | 
|
| 
         | 
|
| 42 | 
         
             
              /*
         
     | 
| 43 | 
         | 
| 44 | 
         
            -
              const setPage = useStore( 
     | 
| 45 | 
         
             
              const pageRef = useRef<HTMLDivElement>(null)
         
     | 
| 46 | 
         | 
| 47 | 
         
             
              useEffect(() => {
         
     | 
| 
         | 
|
| 7 | 
         
             
            import { cn } from "@/lib/utils"
         
     | 
| 8 | 
         | 
| 9 | 
         
             
            export function Page({ page }: { page: number }) {
         
     | 
| 10 | 
         
            +
              const zoomLevel = useStore(s => s.zoomLevel)
         
     | 
| 11 | 
         
            +
              const layouts = useStore(s => s.layouts)
         
     | 
| 12 | 
         | 
| 13 | 
         
             
              // attention: here we use a fallback to layouts[0]
         
     | 
| 14 | 
         
             
              // if no predetermined layout exists for this page number
         
     | 
| 
         | 
|
| 39 | 
         
             
              // this was used to keep track of the page HTML element,
         
     | 
| 40 | 
         
             
              // for use with a HTML-to-bitmap library
         
     | 
| 41 | 
         
             
              // but the CSS layout wasn't followed properly and it depended on the zoom level
         
     | 
| 42 | 
         
            +
              //
         
     | 
| 43 | 
         
            +
              // update: in the future if we want a good html to image convertion
         
     | 
| 44 | 
         
             
              /*
         
     | 
| 45 | 
         | 
| 46 | 
         
            +
              const setPage = useStore(s => s.setPage)
         
     | 
| 47 | 
         
             
              const pageRef = useRef<HTMLDivElement>(null)
         
     | 
| 48 | 
         | 
| 49 | 
         
             
              useEffect(() => {
         
     | 
    	
        src/app/interface/panel/bubble/index.tsx
    CHANGED
    
    | 
         @@ -14,8 +14,8 @@ export function Bubble({ children, onChange }: { 
     | 
|
| 14 | 
         
             
            }) {
         
     | 
| 15 | 
         | 
| 16 | 
         
             
              const ref = useRef<HTMLDivElement>(null)
         
     | 
| 17 | 
         
            -
              const zoomLevel = useStore( 
     | 
| 18 | 
         
            -
              const showCaptions = useStore( 
     | 
| 19 | 
         | 
| 20 | 
         
             
              const text = useRef(`${children || ''}`)
         
     | 
| 21 | 
         | 
| 
         | 
|
| 14 | 
         
             
            }) {
         
     | 
| 15 | 
         | 
| 16 | 
         
             
              const ref = useRef<HTMLDivElement>(null)
         
     | 
| 17 | 
         
            +
              const zoomLevel = useStore(s => s.zoomLevel)
         
     | 
| 18 | 
         
            +
              const showCaptions = useStore(s => s.showCaptions)
         
     | 
| 19 | 
         | 
| 20 | 
         
             
              const text = useRef(`${children || ''}`)
         
     | 
| 21 | 
         | 
    	
        src/app/interface/panel/index.tsx
    CHANGED
    
    | 
         @@ -53,27 +53,27 @@ export function Panel({ 
     | 
|
| 53 | 
         | 
| 54 | 
         
             
              const [mouseOver, setMouseOver] = useState(false)
         
     | 
| 55 | 
         
             
              const ref = useRef<HTMLImageElement>(null)
         
     | 
| 56 | 
         
            -
              const font = useStore( 
     | 
| 57 | 
         
            -
              const preset = useStore( 
     | 
| 58 | 
         | 
| 59 | 
         
            -
              const setGeneratingImages = useStore( 
     | 
| 60 | 
         | 
| 61 | 
         
            -
              const panels = useStore( 
     | 
| 62 | 
         
             
              const prompt = panels[panelIndex] || ""
         
     | 
| 63 | 
         | 
| 64 | 
         
            -
              const setPanelPrompt = useStore( 
     | 
| 65 | 
         | 
| 66 | 
         
            -
              const captions = useStore( 
     | 
| 67 | 
         
             
              const caption = captions[panelIndex] || ""
         
     | 
| 68 | 
         
            -
              const setPanelCaption = useStore( 
     | 
| 69 | 
         | 
| 70 | 
         
            -
              const zoomLevel = useStore( 
     | 
| 71 | 
         | 
| 72 | 
         
            -
              const addToUpscaleQueue = useStore( 
     | 
| 73 | 
         | 
| 74 | 
         
             
              const [_isPending, startTransition] = useTransition()
         
     | 
| 75 | 
         
            -
              const renderedScenes = useStore( 
     | 
| 76 | 
         
            -
              const setRendered = useStore( 
     | 
| 77 | 
         | 
| 78 | 
         
             
              const rendered = renderedScenes[panelIndex] || getInitialRenderedScene()
         
     | 
| 79 | 
         | 
| 
         @@ -288,6 +288,8 @@ export function Panel({ 
     | 
|
| 288 | 
         | 
| 289 | 
         
             
                const renderedScene: RenderedScene | undefined = useStore.getState().renderedScenes[panelIndex]
         
     | 
| 290 | 
         | 
| 
         | 
|
| 
         | 
|
| 291 | 
         
             
                // I'm trying to find a rule to handle the case were we load a .clap file
         
     | 
| 292 | 
         
             
                // I think we should trash all the Panel objects for this to work properly 
         
     | 
| 293 | 
         
             
                if (renderedScene && renderedScene.status === "pregenerated" && renderedScene.assetUrl) {
         
     | 
| 
         | 
|
| 53 | 
         | 
| 54 | 
         
             
              const [mouseOver, setMouseOver] = useState(false)
         
     | 
| 55 | 
         
             
              const ref = useRef<HTMLImageElement>(null)
         
     | 
| 56 | 
         
            +
              const font = useStore(s => s.font)
         
     | 
| 57 | 
         
            +
              const preset = useStore(s => s.preset)
         
     | 
| 58 | 
         | 
| 59 | 
         
            +
              const setGeneratingImages = useStore(s => s.setGeneratingImages)
         
     | 
| 60 | 
         | 
| 61 | 
         
            +
              const panels = useStore(s => s.panels)
         
     | 
| 62 | 
         
             
              const prompt = panels[panelIndex] || ""
         
     | 
| 63 | 
         | 
| 64 | 
         
            +
              const setPanelPrompt = useStore(s => s.setPanelPrompt)
         
     | 
| 65 | 
         | 
| 66 | 
         
            +
              const captions = useStore(s => s.captions)
         
     | 
| 67 | 
         
             
              const caption = captions[panelIndex] || ""
         
     | 
| 68 | 
         
            +
              const setPanelCaption = useStore(s => s.setPanelCaption)
         
     | 
| 69 | 
         | 
| 70 | 
         
            +
              const zoomLevel = useStore(s => s.zoomLevel)
         
     | 
| 71 | 
         | 
| 72 | 
         
            +
              const addToUpscaleQueue = useStore(s => s.addToUpscaleQueue)
         
     | 
| 73 | 
         | 
| 74 | 
         
             
              const [_isPending, startTransition] = useTransition()
         
     | 
| 75 | 
         
            +
              const renderedScenes = useStore(s => s.renderedScenes)
         
     | 
| 76 | 
         
            +
              const setRendered = useStore(s => s.setRendered)
         
     | 
| 77 | 
         | 
| 78 | 
         
             
              const rendered = renderedScenes[panelIndex] || getInitialRenderedScene()
         
     | 
| 79 | 
         | 
| 
         | 
|
| 288 | 
         | 
| 289 | 
         
             
                const renderedScene: RenderedScene | undefined = useStore.getState().renderedScenes[panelIndex]
         
     | 
| 290 | 
         | 
| 291 | 
         
            +
                // console.log("renderedScene:", renderedScene)
         
     | 
| 292 | 
         
            +
             
     | 
| 293 | 
         
             
                // I'm trying to find a rule to handle the case were we load a .clap file
         
     | 
| 294 | 
         
             
                // I think we should trash all the Panel objects for this to work properly 
         
     | 
| 295 | 
         
             
                if (renderedScene && renderedScene.status === "pregenerated" && renderedScene.assetUrl) {
         
     | 
    	
        src/app/interface/select-global-layout/index.tsx
    ADDED
    
    | 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            "use client"
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            import { useEffect, useState } from "react"
         
     | 
| 4 | 
         
            +
            import { useSearchParams } from "next/navigation"
         
     | 
| 5 | 
         
            +
             
     | 
| 6 | 
         
            +
            import { useStore } from "@/app/store"
         
     | 
| 7 | 
         
            +
            import { LayoutName, defaultLayout, nonRandomLayouts } from "@/app/layouts"
         
     | 
| 8 | 
         
            +
            import { useIsBusy } from "@/lib/useIsBusy"
         
     | 
| 9 | 
         
            +
             
     | 
| 10 | 
         
            +
            import { SelectLayout } from "../select-layout"
         
     | 
| 11 | 
         
            +
             
     | 
| 12 | 
         
            +
            export function SelectGlobalLayout() {
         
     | 
| 13 | 
         
            +
              const searchParams = useSearchParams()
         
     | 
| 14 | 
         
            +
             
     | 
| 15 | 
         
            +
              const requestedLayout = (searchParams?.get('layout') as LayoutName) || defaultLayout
         
     | 
| 16 | 
         
            +
             
     | 
| 17 | 
         
            +
              const layout = useStore(s => s.layout)
         
     | 
| 18 | 
         
            +
              const setLayout = useStore(s => s.setLayout)
         
     | 
| 19 | 
         
            +
             
     | 
| 20 | 
         
            +
              const isBusy = useIsBusy()
         
     | 
| 21 | 
         
            +
             
     | 
| 22 | 
         
            +
              const [draftLayout, setDraftLayout] = useState<LayoutName>(requestedLayout)
         
     | 
| 23 | 
         
            +
              
         
     | 
| 24 | 
         
            +
              useEffect(() => {
         
     | 
| 25 | 
         
            +
                const layoutChanged = draftLayout !== layout
         
     | 
| 26 | 
         
            +
                if (layoutChanged && !isBusy) {
         
     | 
| 27 | 
         
            +
                  setLayout(draftLayout)
         
     | 
| 28 | 
         
            +
                }
         
     | 
| 29 | 
         
            +
              }, [layout, draftLayout, isBusy])
         
     | 
| 30 | 
         
            +
                
         
     | 
| 31 | 
         
            +
              return (
         
     | 
| 32 | 
         
            +
                <SelectLayout
         
     | 
| 33 | 
         
            +
                  defaultValue={defaultLayout}
         
     | 
| 34 | 
         
            +
                  onLayoutChange={setDraftLayout}
         
     | 
| 35 | 
         
            +
                  disabled={isBusy}
         
     | 
| 36 | 
         
            +
                  layouts={nonRandomLayouts}
         
     | 
| 37 | 
         
            +
                />
         
     | 
| 38 | 
         
            +
              )
         
     | 
| 39 | 
         
            +
            }
         
     | 
    	
        src/app/interface/select-layout/index.tsx
    ADDED
    
    | 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            "use client"
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            import Image from "next/image"
         
     | 
| 4 | 
         
            +
             
     | 
| 5 | 
         
            +
            import {
         
     | 
| 6 | 
         
            +
              Select,
         
     | 
| 7 | 
         
            +
              SelectContent,
         
     | 
| 8 | 
         
            +
              SelectItem,
         
     | 
| 9 | 
         
            +
              SelectTrigger,
         
     | 
| 10 | 
         
            +
              SelectValue,
         
     | 
| 11 | 
         
            +
            } from "@/components/ui/select"
         
     | 
| 12 | 
         
            +
            import { LayoutName, allLayoutLabels, defaultLayout, layoutIcons } from "@/app/layouts"
         
     | 
| 13 | 
         
            +
             
     | 
| 14 | 
         
            +
            export function SelectLayout({
         
     | 
| 15 | 
         
            +
              defaultValue = defaultLayout,
         
     | 
| 16 | 
         
            +
              onLayoutChange,
         
     | 
| 17 | 
         
            +
              disabled = false,
         
     | 
| 18 | 
         
            +
              layouts = [],
         
     | 
| 19 | 
         
            +
            }: {
         
     | 
| 20 | 
         
            +
              defaultValue?: string | undefined
         
     | 
| 21 | 
         
            +
              onLayoutChange?: ((name: LayoutName) => void)
         
     | 
| 22 | 
         
            +
              disabled?: boolean
         
     | 
| 23 | 
         
            +
              layouts: string[]
         
     | 
| 24 | 
         
            +
            }) {
         
     | 
| 25 | 
         
            +
              return (
         
     | 
| 26 | 
         
            +
                <Select
         
     | 
| 27 | 
         
            +
                  defaultValue={defaultValue}
         
     | 
| 28 | 
         
            +
                  onValueChange={(name) => { onLayoutChange?.(name as LayoutName) }}
         
     | 
| 29 | 
         
            +
                  disabled={disabled}
         
     | 
| 30 | 
         
            +
                  >
         
     | 
| 31 | 
         
            +
                  <SelectTrigger className="flex-grow bg-gray-100 text-gray-700 dark:bg-gray-100 dark:text-gray-700">
         
     | 
| 32 | 
         
            +
                    <SelectValue className="text-2xs md:text-sm" placeholder="Layout" />
         
     | 
| 33 | 
         
            +
                  </SelectTrigger>
         
     | 
| 34 | 
         
            +
                  <SelectContent>
         
     | 
| 35 | 
         
            +
                    {layouts.map(key =>
         
     | 
| 36 | 
         
            +
                      <SelectItem key={key} value={key} className="w-full">
         
     | 
| 37 | 
         
            +
                        <div className="space-x-6 flex flex-row items-center justify-between">
         
     | 
| 38 | 
         
            +
                          <div className="flex">{
         
     | 
| 39 | 
         
            +
                            (allLayoutLabels as any)[key]
         
     | 
| 40 | 
         
            +
                          }</div>
         
     | 
| 41 | 
         
            +
                       
         
     | 
| 42 | 
         
            +
                          {(layoutIcons as any)[key]
         
     | 
| 43 | 
         
            +
                            ? <Image
         
     | 
| 44 | 
         
            +
                                className="rounded-sm opacity-75"
         
     | 
| 45 | 
         
            +
                                src={(layoutIcons as any)[key]}
         
     | 
| 46 | 
         
            +
                                width={20}
         
     | 
| 47 | 
         
            +
                                height={18}
         
     | 
| 48 | 
         
            +
                                alt={key}
         
     | 
| 49 | 
         
            +
                            /> : null}
         
     | 
| 50 | 
         
            +
                        </div>
         
     | 
| 51 | 
         
            +
                      </SelectItem>
         
     | 
| 52 | 
         
            +
                    )}
         
     | 
| 53 | 
         
            +
                  </SelectContent>
         
     | 
| 54 | 
         
            +
                </Select>
         
     | 
| 55 | 
         
            +
              )
         
     | 
| 56 | 
         
            +
            }
         
     | 
    	
        src/app/interface/share/index.tsx
    CHANGED
    
    | 
         @@ -6,9 +6,9 @@ import { useState } from "react" 
     | 
|
| 6 | 
         | 
| 7 | 
         
             
            export function Share() {
         
     | 
| 8 | 
         
             
              const [isOpen, setOpen] = useState(false)
         
     | 
| 9 | 
         
            -
              const preset = useStore( 
     | 
| 10 | 
         
            -
              const prompt = useStore( 
     | 
| 11 | 
         
            -
              const panelGenerationStatus = useStore( 
     | 
| 12 | 
         
             
              const allStatus = Object.values(panelGenerationStatus)
         
     | 
| 13 | 
         
             
              const remainingImages = allStatus.reduce((acc, s) => (acc + (s ? 1 : 0)), 0)
         
     | 
| 14 | 
         | 
| 
         | 
|
| 6 | 
         | 
| 7 | 
         
             
            export function Share() {
         
     | 
| 8 | 
         
             
              const [isOpen, setOpen] = useState(false)
         
     | 
| 9 | 
         
            +
              const preset = useStore(s => s.preset)
         
     | 
| 10 | 
         
            +
              const prompt = useStore(s => s.prompt)
         
     | 
| 11 | 
         
            +
              const panelGenerationStatus = useStore(s => s.panelGenerationStatus)
         
     | 
| 12 | 
         
             
              const allStatus = Object.values(panelGenerationStatus)
         
     | 
| 13 | 
         
             
              const remainingImages = allStatus.reduce((acc, s) => (acc + (s ? 1 : 0)), 0)
         
     | 
| 14 | 
         | 
    	
        src/app/interface/top-menu/index.tsx
    CHANGED
    
    | 
         @@ -2,7 +2,6 @@ 
     | 
|
| 2 | 
         | 
| 3 | 
         
             
            import { useEffect, useState } from "react"
         
     | 
| 4 | 
         
             
            import { useSearchParams } from "next/navigation"
         
     | 
| 5 | 
         
            -
            import Image from "next/image"
         
     | 
| 6 | 
         
             
            import { StaticImageData } from "next/image"
         
     | 
| 7 | 
         
             
            import { useLocalStorage } from "usehooks-ts"
         
     | 
| 8 | 
         | 
| 
         @@ -20,25 +19,15 @@ import { Input } from "@/components/ui/input" 
     | 
|
| 20 | 
         
             
            import { PresetName, defaultPreset, nonRandomPresets, presets } from "@/app/engine/presets"
         
     | 
| 21 | 
         
             
            import { useStore } from "@/app/store"
         
     | 
| 22 | 
         
             
            import { Button } from "@/components/ui/button"
         
     | 
| 23 | 
         
            -
            import { LayoutName,  
     | 
| 24 | 
         
             
            import { Switch } from "@/components/ui/switch"
         
     | 
| 25 | 
         
             
            import { useOAuth } from "@/lib/useOAuth"
         
     | 
| 
         | 
|
| 26 | 
         | 
| 27 | 
         
            -
            import layoutPreview0 from "../../../../public/layouts/layout0.jpg"
         
     | 
| 28 | 
         
            -
            import layoutPreview1 from "../../../../public/layouts/layout1.jpg"
         
     | 
| 29 | 
         
            -
            import layoutPreview2 from "../../../../public/layouts/layout2.jpg"
         
     | 
| 30 | 
         
            -
            import layoutPreview3 from "../../../../public/layouts/layout3.jpg"
         
     | 
| 31 | 
         
             
            import { localStorageKeys } from "../settings-dialog/localStorageKeys"
         
     | 
| 32 | 
         
             
            import { defaultSettings } from "../settings-dialog/defaultSettings"
         
     | 
| 33 | 
         
             
            import { AuthWall } from "../auth-wall"
         
     | 
| 34 | 
         
            -
             
     | 
| 35 | 
         
            -
            const layoutIcons: Partial<Record<LayoutName, StaticImageData>> = {
         
     | 
| 36 | 
         
            -
              Layout0: layoutPreview0,
         
     | 
| 37 | 
         
            -
              Layout1: layoutPreview1,
         
     | 
| 38 | 
         
            -
              Layout2: layoutPreview2,
         
     | 
| 39 | 
         
            -
              Layout3: layoutPreview3,
         
     | 
| 40 | 
         
            -
              Layout4: undefined,
         
     | 
| 41 | 
         
            -
            }
         
     | 
| 42 | 
         | 
| 43 | 
         
             
            export function TopMenu() {
         
     | 
| 44 | 
         
             
              const searchParams = useSearchParams()
         
     | 
| 
         @@ -49,25 +38,22 @@ export function TopMenu() { 
     | 
|
| 49 | 
         
             
              const requestedStoryPrompt = (searchParams?.get('storyPrompt') as string) || ""
         
     | 
| 50 | 
         
             
              const requestedLayout = (searchParams?.get('layout') as LayoutName) || defaultLayout
         
     | 
| 51 | 
         | 
| 52 | 
         
            -
               // const font = useStore( 
     | 
| 53 | 
         
            -
              // const setFont = useStore( 
     | 
| 54 | 
         
            -
              const preset = useStore( 
     | 
| 55 | 
         
            -
              const prompt = useStore( 
     | 
| 56 | 
         
            -
              const layout = useStore( 
     | 
| 57 | 
         
            -
              const setLayout = useStore( 
     | 
| 58 | 
         
            -
             
     | 
| 59 | 
         
            -
              const setShowCaptions = useStore(state => state.setShowCaptions)
         
     | 
| 60 | 
         
            -
              const showCaptions = useStore(state => state.showCaptions)
         
     | 
| 61 | 
         | 
| 62 | 
         
            -
              const  
     | 
| 63 | 
         
            -
              const  
     | 
| 64 | 
         | 
| 65 | 
         
            -
              const  
     | 
| 
         | 
|
| 66 | 
         | 
| 67 | 
         
            -
              const  
     | 
| 68 | 
         
            -
              const atLeastOnePanelIsBusy = useStore(state => state.atLeastOnePanelIsBusy)
         
     | 
| 69 | 
         
            -
              const isBusy = isGeneratingStory || atLeastOnePanelIsBusy
         
     | 
| 70 | 
         | 
| 
         | 
|
| 71 | 
         | 
| 72 | 
         
             
              const [lastDraftPromptA, setLastDraftPromptA] = useLocalStorage<string>(
         
     | 
| 73 | 
         
             
                "AI_COMIC_FACTORY_LAST_DRAFT_PROMPT_A",
         
     | 
| 
         @@ -167,36 +153,12 @@ export function TopMenu() { 
     | 
|
| 167 | 
         | 
| 168 | 
         
             
                      {/* <Label className="flex text-2xs md:text-sm md:w-24">Style:</Label> */}
         
     | 
| 169 | 
         | 
| 170 | 
         
            -
                      < 
     | 
| 171 | 
         
             
                        defaultValue={defaultLayout}
         
     | 
| 172 | 
         
            -
                         
     | 
| 173 | 
         
             
                        disabled={isBusy}
         
     | 
| 174 | 
         
            -
                         
     | 
| 175 | 
         
            -
             
     | 
| 176 | 
         
            -
                          <SelectValue className="text-2xs md:text-sm" placeholder="Layout" />
         
     | 
| 177 | 
         
            -
                        </SelectTrigger>
         
     | 
| 178 | 
         
            -
                        <SelectContent>
         
     | 
| 179 | 
         
            -
                          {nonRandomLayouts.map(key =>
         
     | 
| 180 | 
         
            -
                            <SelectItem key={key} value={key} className="w-full">
         
     | 
| 181 | 
         
            -
                              <div className="space-x-6 flex flex-row items-center justify-between">
         
     | 
| 182 | 
         
            -
                                <div className="flex">{
         
     | 
| 183 | 
         
            -
                                  (allLayoutLabels as any)[key]
         
     | 
| 184 | 
         
            -
                                }</div>
         
     | 
| 185 | 
         
            -
                             
         
     | 
| 186 | 
         
            -
                                  {(layoutIcons as any)[key]
         
     | 
| 187 | 
         
            -
                                    ? <Image
         
     | 
| 188 | 
         
            -
                                        className="rounded-sm opacity-75"
         
     | 
| 189 | 
         
            -
                                        src={(layoutIcons as any)[key]}
         
     | 
| 190 | 
         
            -
                                        width={20}
         
     | 
| 191 | 
         
            -
                                        height={18}
         
     | 
| 192 | 
         
            -
                                        alt={key}
         
     | 
| 193 | 
         
            -
                                    /> : null}
         
     | 
| 194 | 
         
            -
                          
         
     | 
| 195 | 
         
            -
                              </div>
         
     | 
| 196 | 
         
            -
                            </SelectItem>
         
     | 
| 197 | 
         
            -
                          )}
         
     | 
| 198 | 
         
            -
                        </SelectContent>
         
     | 
| 199 | 
         
            -
                      </Select>
         
     | 
| 200 | 
         
             
                    </div>
         
     | 
| 201 | 
         
             
                    <div className="flex flex-row items-center space-x-3">
         
     | 
| 202 | 
         
             
                    <Switch
         
     | 
| 
         | 
|
| 2 | 
         | 
| 3 | 
         
             
            import { useEffect, useState } from "react"
         
     | 
| 4 | 
         
             
            import { useSearchParams } from "next/navigation"
         
     | 
| 
         | 
|
| 5 | 
         
             
            import { StaticImageData } from "next/image"
         
     | 
| 6 | 
         
             
            import { useLocalStorage } from "usehooks-ts"
         
     | 
| 7 | 
         | 
| 
         | 
|
| 19 | 
         
             
            import { PresetName, defaultPreset, nonRandomPresets, presets } from "@/app/engine/presets"
         
     | 
| 20 | 
         
             
            import { useStore } from "@/app/store"
         
     | 
| 21 | 
         
             
            import { Button } from "@/components/ui/button"
         
     | 
| 22 | 
         
            +
            import { LayoutName, defaultLayout, nonRandomLayouts } from "@/app/layouts"
         
     | 
| 23 | 
         
             
            import { Switch } from "@/components/ui/switch"
         
     | 
| 24 | 
         
             
            import { useOAuth } from "@/lib/useOAuth"
         
     | 
| 25 | 
         
            +
            import { useIsBusy } from "@/lib/useIsBusy"
         
     | 
| 26 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 27 | 
         
             
            import { localStorageKeys } from "../settings-dialog/localStorageKeys"
         
     | 
| 28 | 
         
             
            import { defaultSettings } from "../settings-dialog/defaultSettings"
         
     | 
| 29 | 
         
             
            import { AuthWall } from "../auth-wall"
         
     | 
| 30 | 
         
            +
            import { SelectLayout } from "../select-layout"
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 31 | 
         | 
| 32 | 
         
             
            export function TopMenu() {
         
     | 
| 33 | 
         
             
              const searchParams = useSearchParams()
         
     | 
| 
         | 
|
| 38 | 
         
             
              const requestedStoryPrompt = (searchParams?.get('storyPrompt') as string) || ""
         
     | 
| 39 | 
         
             
              const requestedLayout = (searchParams?.get('layout') as LayoutName) || defaultLayout
         
     | 
| 40 | 
         | 
| 41 | 
         
            +
               // const font = useStore(s => s.font)
         
     | 
| 42 | 
         
            +
              // const setFont = useStore(s => s.setFont)
         
     | 
| 43 | 
         
            +
              const preset = useStore(s => s.preset)
         
     | 
| 44 | 
         
            +
              const prompt = useStore(s => s.prompt)
         
     | 
| 45 | 
         
            +
              const layout = useStore(s => s.layout)
         
     | 
| 46 | 
         
            +
              const setLayout = useStore(s => s.setLayout)
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 47 | 
         | 
| 48 | 
         
            +
              const setShowCaptions = useStore(s => s.setShowCaptions)
         
     | 
| 49 | 
         
            +
              const showCaptions = useStore(s => s.showCaptions)
         
     | 
| 50 | 
         | 
| 51 | 
         
            +
              const currentNbPages = useStore(s => s.currentNbPages)
         
     | 
| 52 | 
         
            +
              const setCurrentNbPages = useStore(s => s.setCurrentNbPages)
         
     | 
| 53 | 
         | 
| 54 | 
         
            +
              const generate = useStore(s => s.generate)
         
     | 
| 
         | 
|
| 
         | 
|
| 55 | 
         | 
| 56 | 
         
            +
              const isBusy = useIsBusy()
         
     | 
| 57 | 
         | 
| 58 | 
         
             
              const [lastDraftPromptA, setLastDraftPromptA] = useLocalStorage<string>(
         
     | 
| 59 | 
         
             
                "AI_COMIC_FACTORY_LAST_DRAFT_PROMPT_A",
         
     | 
| 
         | 
|
| 153 | 
         | 
| 154 | 
         
             
                      {/* <Label className="flex text-2xs md:text-sm md:w-24">Style:</Label> */}
         
     | 
| 155 | 
         | 
| 156 | 
         
            +
                      <SelectLayout
         
     | 
| 157 | 
         
             
                        defaultValue={defaultLayout}
         
     | 
| 158 | 
         
            +
                        onLayoutChange={setDraftLayout}
         
     | 
| 159 | 
         
             
                        disabled={isBusy}
         
     | 
| 160 | 
         
            +
                        layouts={nonRandomLayouts}
         
     | 
| 161 | 
         
            +
                      />
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 162 | 
         
             
                    </div>
         
     | 
| 163 | 
         
             
                    <div className="flex flex-row items-center space-x-3">
         
     | 
| 164 | 
         
             
                    <Switch
         
     | 
    	
        src/app/layouts/index.tsx
    CHANGED
    
    | 
         @@ -1,10 +1,17 @@ 
     | 
|
| 1 | 
         
             
            "use client"
         
     | 
| 2 | 
         | 
| 
         | 
|
| 
         | 
|
| 3 | 
         
             
            import { Panel } from "@/app/interface/panel"
         
     | 
| 4 | 
         
             
            import { pick } from "@/lib/pick"
         
     | 
| 5 | 
         
             
            import { Grid } from "@/app/interface/grid"
         
     | 
| 6 | 
         
             
            import { LayoutProps } from "@/types"
         
     | 
| 7 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 8 | 
         
             
            export function Layout0({ page, nbPanels }: LayoutProps) {
         
     | 
| 9 | 
         
             
              return (
         
     | 
| 10 | 
         
             
                <Grid className="grid-cols-2 grid-rows-2">
         
     | 
| 
         @@ -440,3 +447,11 @@ export const getRandomLayoutName = (): LayoutName => { 
     | 
|
| 440 | 
         
             
            export function getRandomLayoutNames(): LayoutName[] {
         
     | 
| 441 | 
         
             
              return nonRandomLayouts.sort(() => Math.random() - 0.5) as LayoutName[]
         
     | 
| 442 | 
         
             
            }
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
             
            "use client"
         
     | 
| 2 | 
         | 
| 3 | 
         
            +
            import { StaticImageData } from "next/image"
         
     | 
| 4 | 
         
            +
             
     | 
| 5 | 
         
             
            import { Panel } from "@/app/interface/panel"
         
     | 
| 6 | 
         
             
            import { pick } from "@/lib/pick"
         
     | 
| 7 | 
         
             
            import { Grid } from "@/app/interface/grid"
         
     | 
| 8 | 
         
             
            import { LayoutProps } from "@/types"
         
     | 
| 9 | 
         | 
| 10 | 
         
            +
            import layoutPreview0 from "../../../public/layouts/layout0.jpg"
         
     | 
| 11 | 
         
            +
            import layoutPreview1 from "../../../public/layouts/layout1.jpg"
         
     | 
| 12 | 
         
            +
            import layoutPreview2 from "../../../public/layouts/layout2.jpg"
         
     | 
| 13 | 
         
            +
            import layoutPreview3 from "../../../public/layouts/layout3.jpg"
         
     | 
| 14 | 
         
            +
             
     | 
| 15 | 
         
             
            export function Layout0({ page, nbPanels }: LayoutProps) {
         
     | 
| 16 | 
         
             
              return (
         
     | 
| 17 | 
         
             
                <Grid className="grid-cols-2 grid-rows-2">
         
     | 
| 
         | 
|
| 447 | 
         
             
            export function getRandomLayoutNames(): LayoutName[] {
         
     | 
| 448 | 
         
             
              return nonRandomLayouts.sort(() => Math.random() - 0.5) as LayoutName[]
         
     | 
| 449 | 
         
             
            }
         
     | 
| 450 | 
         
            +
             
     | 
| 451 | 
         
            +
            export const layoutIcons: Partial<Record<LayoutName, StaticImageData>> = {
         
     | 
| 452 | 
         
            +
              Layout0: layoutPreview0,
         
     | 
| 453 | 
         
            +
              Layout1: layoutPreview1,
         
     | 
| 454 | 
         
            +
              Layout2: layoutPreview2,
         
     | 
| 455 | 
         
            +
              Layout3: layoutPreview3,
         
     | 
| 456 | 
         
            +
              Layout4: undefined,
         
     | 
| 457 | 
         
            +
            }
         
     | 
    	
        src/app/layouts/settings.tsx
    ADDED
    
    | 
         @@ -0,0 +1,52 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            import { ClapMediaOrientation } from "@aitube/clap"
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            import { LayoutName } from "."
         
     | 
| 4 | 
         
            +
             
     | 
| 5 | 
         
            +
            export type LayoutSettings = {
         
     | 
| 6 | 
         
            +
              panel: number
         
     | 
| 7 | 
         
            +
              orientation: ClapMediaOrientation
         
     | 
| 8 | 
         
            +
              width: number
         
     | 
| 9 | 
         
            +
              height: number
         
     | 
| 10 | 
         
            +
            }
         
     | 
| 11 | 
         
            +
             
     | 
| 12 | 
         
            +
            export const layouts: Record<LayoutName, LayoutSettings[]> = {
         
     | 
| 13 | 
         
            +
              random: [],
         
     | 
| 14 | 
         
            +
              Layout0: [
         
     | 
| 15 | 
         
            +
                { panel: 0, orientation: ClapMediaOrientation.SQUARE, width: 1024, height: 1024 },
         
     | 
| 16 | 
         
            +
                { panel: 1, orientation: ClapMediaOrientation.SQUARE, width: 1024, height: 1024 },
         
     | 
| 17 | 
         
            +
                { panel: 2, orientation: ClapMediaOrientation.SQUARE, width: 1024, height: 1024 },
         
     | 
| 18 | 
         
            +
                { panel: 3, orientation: ClapMediaOrientation.SQUARE, width: 1024, height: 1024 },
         
     | 
| 19 | 
         
            +
              ],
         
     | 
| 20 | 
         
            +
              Layout1: [
         
     | 
| 21 | 
         
            +
                { panel: 0, orientation: ClapMediaOrientation.LANDSCAPE, width: 1024, height: 768 },
         
     | 
| 22 | 
         
            +
                { panel: 1, orientation: ClapMediaOrientation.PORTRAIT, width: 768, height: 1024 },
         
     | 
| 23 | 
         
            +
                { panel: 2, orientation: ClapMediaOrientation.PORTRAIT, width: 768, height: 1024 },
         
     | 
| 24 | 
         
            +
                { panel: 3, orientation: ClapMediaOrientation.LANDSCAPE, width: 1024, height: 768 },
         
     | 
| 25 | 
         
            +
              ],
         
     | 
| 26 | 
         
            +
              Layout2: [
         
     | 
| 27 | 
         
            +
                { panel: 0, orientation: ClapMediaOrientation.PORTRAIT, width: 768, height: 1024 },
         
     | 
| 28 | 
         
            +
                { panel: 1, orientation: ClapMediaOrientation.PORTRAIT, width: 768, height: 1024 },
         
     | 
| 29 | 
         
            +
                { panel: 2, orientation: ClapMediaOrientation.PORTRAIT, width: 512, height: 1024 },
         
     | 
| 30 | 
         
            +
                { panel: 3, orientation: ClapMediaOrientation.LANDSCAPE, width: 1024, height: 768 },
         
     | 
| 31 | 
         
            +
              ],
         
     | 
| 32 | 
         
            +
              Layout3: [
         
     | 
| 33 | 
         
            +
                { panel: 0, orientation: ClapMediaOrientation.LANDSCAPE, width: 1024, height: 768 },
         
     | 
| 34 | 
         
            +
                { panel: 1, orientation: ClapMediaOrientation.PORTRAIT, width: 768, height: 1024 },
         
     | 
| 35 | 
         
            +
                { panel: 2, orientation: ClapMediaOrientation.PORTRAIT, width: 768, height: 1024 },
         
     | 
| 36 | 
         
            +
                { panel: 3, orientation: ClapMediaOrientation.LANDSCAPE, width: 1024, height: 768 },
         
     | 
| 37 | 
         
            +
              ],
         
     | 
| 38 | 
         
            +
              Layout4: [
         
     | 
| 39 | 
         
            +
                { panel: 0, orientation: ClapMediaOrientation.PORTRAIT, width: 512, height: 1024 },
         
     | 
| 40 | 
         
            +
                { panel: 1, orientation: ClapMediaOrientation.LANDSCAPE, width: 1024, height: 768 },
         
     | 
| 41 | 
         
            +
                { panel: 2, orientation: ClapMediaOrientation.PORTRAIT, width: 768, height: 1024 },
         
     | 
| 42 | 
         
            +
                { panel: 3, orientation: ClapMediaOrientation.LANDSCAPE, width: 1024, height: 512 },
         
     | 
| 43 | 
         
            +
              ],
         
     | 
| 44 | 
         
            +
            }
         
     | 
| 45 | 
         
            +
            /*
         
     | 
| 46 | 
         
            +
            Layout5: [
         
     | 
| 47 | 
         
            +
              { panel: 0, orientation: ClapMediaOrientation.SQUARE, width: 1024, height: 1024 },
         
     | 
| 48 | 
         
            +
              { panel: 1, orientation: ClapMediaOrientation.SQUARE, width: 1024, height: 1024 },
         
     | 
| 49 | 
         
            +
              { panel: 2, orientation: ClapMediaOrientation.SQUARE, width: 1024, height: 1024 },
         
     | 
| 50 | 
         
            +
              { panel: 3, orientation: ClapMediaOrientation.SQUARE, width: 1024, height: 1024 },
         
     | 
| 51 | 
         
            +
            ]
         
     | 
| 52 | 
         
            +
            */
         
     | 
    	
        src/app/main.tsx
    CHANGED
    
    | 
         @@ -63,7 +63,7 @@ export default function Main() { 
     | 
|
| 63 | 
         
             
              )
         
     | 
| 64 | 
         | 
| 65 | 
         
             
              const numberOfPanels = Object.keys(panels).length
         
     | 
| 66 | 
         
            -
              const panelGenerationStatus = useStore( 
     | 
| 67 | 
         
             
              const allStatus = Object.values(panelGenerationStatus)
         
     | 
| 68 | 
         
             
              const numberOfPendingGenerations = allStatus.reduce((acc, s) => (acc + (s ? 1 : 0)), 0)
         
     | 
| 69 | 
         | 
| 
         @@ -121,9 +121,12 @@ export default function Main() { 
     | 
|
| 121 | 
         
             
                // console.log(`main.tsx: asked to re-generate!!`)
         
     | 
| 122 | 
         
             
                if (!prompt) { return }
         
     | 
| 123 | 
         | 
| 
         | 
|
| 124 | 
         
             
                // a quick and dirty hack to skip prompt regeneration,
         
     | 
| 125 | 
         
             
                // unless the prompt has really changed
         
     | 
| 126 | 
         
            -
                if ( 
     | 
| 
         | 
|
| 
         | 
|
| 127 | 
         
             
                  console.log(`loading a pre-generated comic, so skipping prompt regeneration..`)
         
     | 
| 128 | 
         
             
                  return
         
     | 
| 129 | 
         
             
                }
         
     | 
| 
         | 
|
| 63 | 
         
             
              )
         
     | 
| 64 | 
         | 
| 65 | 
         
             
              const numberOfPanels = Object.keys(panels).length
         
     | 
| 66 | 
         
            +
              const panelGenerationStatus = useStore(s => s.panelGenerationStatus)
         
     | 
| 67 | 
         
             
              const allStatus = Object.values(panelGenerationStatus)
         
     | 
| 68 | 
         
             
              const numberOfPendingGenerations = allStatus.reduce((acc, s) => (acc + (s ? 1 : 0)), 0)
         
     | 
| 69 | 
         | 
| 
         | 
|
| 121 | 
         
             
                // console.log(`main.tsx: asked to re-generate!!`)
         
     | 
| 122 | 
         
             
                if (!prompt) { return }
         
     | 
| 123 | 
         | 
| 124 | 
         
            +
             
     | 
| 125 | 
         
             
                // a quick and dirty hack to skip prompt regeneration,
         
     | 
| 126 | 
         
             
                // unless the prompt has really changed
         
     | 
| 127 | 
         
            +
                if (
         
     | 
| 128 | 
         
            +
                  prompt === useStore.getState().currentClap?.meta.description
         
     | 
| 129 | 
         
            +
                ) {
         
     | 
| 130 | 
         
             
                  console.log(`loading a pre-generated comic, so skipping prompt regeneration..`)
         
     | 
| 131 | 
         
             
                  return
         
     | 
| 132 | 
         
             
                }
         
     | 
    	
        src/app/queries/getDynamicConfig.ts
    CHANGED
    
    | 
         @@ -15,7 +15,10 @@ export async function getDynamicConfig(): Promise<DynamicConfig> { 
     | 
|
| 15 | 
         
             
                nbPanelsPerPage,
         
     | 
| 16 | 
         
             
                nbTotalPanelsToGenerate,
         
     | 
| 17 | 
         
             
                oauthClientId: getValidString(process.env.HUGGING_FACE_OAUTH_CLIENT_ID, ""),
         
     | 
| 
         | 
|
| 
         | 
|
| 18 | 
         
             
                oauthRedirectUrl: getValidString(process.env.HUGGING_FACE_OAUTH_REDIRECT_URL, ""),
         
     | 
| 
         | 
|
| 19 | 
         
             
                oauthScopes: "openid profile inference-api",
         
     | 
| 20 | 
         
             
                enableHuggingFaceOAuth: getValidBoolean(process.env.ENABLE_HUGGING_FACE_OAUTH, false),
         
     | 
| 21 | 
         
             
                enableHuggingFaceOAuthWall: getValidBoolean(process.env.ENABLE_HUGGING_FACE_OAUTH_WALL, false),
         
     | 
| 
         | 
|
| 15 | 
         
             
                nbPanelsPerPage,
         
     | 
| 16 | 
         
             
                nbTotalPanelsToGenerate,
         
     | 
| 17 | 
         
             
                oauthClientId: getValidString(process.env.HUGGING_FACE_OAUTH_CLIENT_ID, ""),
         
     | 
| 18 | 
         
            +
             
     | 
| 19 | 
         
            +
                // this doesn't work (conceptually)
         
     | 
| 20 | 
         
             
                oauthRedirectUrl: getValidString(process.env.HUGGING_FACE_OAUTH_REDIRECT_URL, ""),
         
     | 
| 21 | 
         
            +
             
     | 
| 22 | 
         
             
                oauthScopes: "openid profile inference-api",
         
     | 
| 23 | 
         
             
                enableHuggingFaceOAuth: getValidBoolean(process.env.ENABLE_HUGGING_FACE_OAUTH, false),
         
     | 
| 24 | 
         
             
                enableHuggingFaceOAuthWall: getValidBoolean(process.env.ENABLE_HUGGING_FACE_OAUTH_WALL, false),
         
     | 
    	
        src/app/store/index.ts
    CHANGED
    
    | 
         @@ -10,6 +10,8 @@ import { getParam } from "@/lib/getParam" 
     | 
|
| 10 | 
         | 
| 11 | 
         
             
            import { LayoutName, defaultLayout, getRandomLayoutName } from "../layouts"
         
     | 
| 12 | 
         
             
            import { putTextInInput } from "@/lib/putTextInInput"
         
     | 
| 
         | 
|
| 
         | 
|
| 13 | 
         | 
| 14 | 
         
             
            export const useStore = create<{
         
     | 
| 15 | 
         
             
              prompt: string
         
     | 
| 
         @@ -54,7 +56,7 @@ export const useStore = create<{ 
     | 
|
| 54 | 
         
             
              setPanels: (panels: string[]) => void
         
     | 
| 55 | 
         
             
              setPanelPrompt: (newPrompt: string, index: number) => void
         
     | 
| 56 | 
         
             
              setShowCaptions: (showCaptions: boolean) => void
         
     | 
| 57 | 
         
            -
              setLayout: (layout: LayoutName) => void
         
     | 
| 58 | 
         
             
              setLayouts: (layouts: LayoutName[]) => void
         
     | 
| 59 | 
         
             
              setCaptions: (captions: string[]) => void
         
     | 
| 60 | 
         
             
              setPanelCaption: (newCaption: string, index: number) => void
         
     | 
| 
         @@ -77,6 +79,8 @@ export const useStore = create<{ 
     | 
|
| 77 | 
         
             
              convertClapToComic: (clap: ClapProject) => Promise<{
         
     | 
| 78 | 
         
             
                currentNbPanels: number
         
     | 
| 79 | 
         
             
                prompt: string
         
     | 
| 
         | 
|
| 
         | 
|
| 80 | 
         
             
                storyPrompt: string
         
     | 
| 81 | 
         
             
                stylePrompt: string
         
     | 
| 82 | 
         
             
                panels: string[]
         
     | 
| 
         @@ -298,15 +302,19 @@ export const useStore = create<{ 
     | 
|
| 298 | 
         
             
                  ))
         
     | 
| 299 | 
         
             
                })
         
     | 
| 300 | 
         
             
              },
         
     | 
| 301 | 
         
            -
              setLayout: (layoutName: LayoutName) => {
         
     | 
| 302 | 
         
            -
                const { maxNbPages, currentNbPanelsPerPage } = get()
         
     | 
| 303 | 
         | 
| 304 | 
         
            -
                const layouts: LayoutName[] = []
         
     | 
| 305 | 
         
             
                for (let i = 0; i < maxNbPages; i++) {
         
     | 
| 306 | 
         
            -
                   
     | 
| 307 | 
         
            -
             
     | 
| 308 | 
         
            -
             
     | 
| 309 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 310 | 
         
             
                }
         
     | 
| 311 | 
         | 
| 312 | 
         
             
                set({
         
     | 
| 
         @@ -511,6 +519,8 @@ export const useStore = create<{ 
     | 
|
| 511 | 
         
             
              convertClapToComic: async (clap: ClapProject): Promise<{
         
     | 
| 512 | 
         
             
                currentNbPanels: number
         
     | 
| 513 | 
         
             
                prompt: string
         
     | 
| 
         | 
|
| 
         | 
|
| 514 | 
         
             
                storyPrompt: string
         
     | 
| 515 | 
         
             
                stylePrompt: string
         
     | 
| 516 | 
         
             
                panels: string[]
         
     | 
| 
         @@ -575,9 +585,12 @@ export const useStore = create<{ 
     | 
|
| 575 | 
         
             
                  captions.push(ui.prompt)
         
     | 
| 576 | 
         
             
                })
         
     | 
| 577 | 
         | 
| 578 | 
         
            -
             
     | 
| 
         | 
|
| 579 | 
         
             
                  currentNbPanels: shots.length,
         
     | 
| 580 | 
         
             
                  prompt,
         
     | 
| 
         | 
|
| 
         | 
|
| 581 | 
         
             
                  storyPrompt,
         
     | 
| 582 | 
         
             
                  stylePrompt,
         
     | 
| 583 | 
         
             
                  panels,
         
     | 
| 
         @@ -595,6 +608,8 @@ export const useStore = create<{ 
     | 
|
| 595 | 
         
             
                const {
         
     | 
| 596 | 
         
             
                  currentNbPanels,
         
     | 
| 597 | 
         
             
                  prompt,
         
     | 
| 
         | 
|
| 
         | 
|
| 598 | 
         
             
                  storyPrompt,
         
     | 
| 599 | 
         
             
                  stylePrompt,
         
     | 
| 600 | 
         
             
                  panels,
         
     | 
| 
         @@ -610,6 +625,8 @@ export const useStore = create<{ 
     | 
|
| 610 | 
         
             
                  currentClap,
         
     | 
| 611 | 
         
             
                  currentNbPanels,
         
     | 
| 612 | 
         
             
                  prompt,
         
     | 
| 
         | 
|
| 
         | 
|
| 613 | 
         
             
                  panels,
         
     | 
| 614 | 
         
             
                  renderedScenes,
         
     | 
| 615 | 
         
             
                  captions,
         
     | 
| 
         | 
|
| 10 | 
         | 
| 11 | 
         
             
            import { LayoutName, defaultLayout, getRandomLayoutName } from "../layouts"
         
     | 
| 12 | 
         
             
            import { putTextInInput } from "@/lib/putTextInInput"
         
     | 
| 13 | 
         
            +
            import { parsePresetFromPrompts } from "@/lib/parsePresetFromPrompts"
         
     | 
| 14 | 
         
            +
            import { parseLayoutFromStoryboards } from "@/lib/parseLayoutFromStoryboards"
         
     | 
| 15 | 
         | 
| 16 | 
         
             
            export const useStore = create<{
         
     | 
| 17 | 
         
             
              prompt: string
         
     | 
| 
         | 
|
| 56 | 
         
             
              setPanels: (panels: string[]) => void
         
     | 
| 57 | 
         
             
              setPanelPrompt: (newPrompt: string, index: number) => void
         
     | 
| 58 | 
         
             
              setShowCaptions: (showCaptions: boolean) => void
         
     | 
| 59 | 
         
            +
              setLayout: (layout: LayoutName, index?: number) => void
         
     | 
| 60 | 
         
             
              setLayouts: (layouts: LayoutName[]) => void
         
     | 
| 61 | 
         
             
              setCaptions: (captions: string[]) => void
         
     | 
| 62 | 
         
             
              setPanelCaption: (newCaption: string, index: number) => void
         
     | 
| 
         | 
|
| 79 | 
         
             
              convertClapToComic: (clap: ClapProject) => Promise<{
         
     | 
| 80 | 
         
             
                currentNbPanels: number
         
     | 
| 81 | 
         
             
                prompt: string
         
     | 
| 82 | 
         
            +
                preset: Preset
         
     | 
| 83 | 
         
            +
                layout: LayoutName
         
     | 
| 84 | 
         
             
                storyPrompt: string
         
     | 
| 85 | 
         
             
                stylePrompt: string
         
     | 
| 86 | 
         
             
                panels: string[]
         
     | 
| 
         | 
|
| 302 | 
         
             
                  ))
         
     | 
| 303 | 
         
             
                })
         
     | 
| 304 | 
         
             
              },
         
     | 
| 305 | 
         
            +
              setLayout: (layoutName: LayoutName, index?: number) => {
         
     | 
| 306 | 
         
            +
                const { maxNbPages, currentNbPanelsPerPage, layouts } = get()
         
     | 
| 307 | 
         | 
| 
         | 
|
| 308 | 
         
             
                for (let i = 0; i < maxNbPages; i++) {
         
     | 
| 309 | 
         
            +
                  let name = layoutName === "random" ? getRandomLayoutName() : layoutName
         
     | 
| 310 | 
         
            +
             
     | 
| 311 | 
         
            +
                  if (typeof index === "number" && !isNaN(index) && isFinite(index)) {
         
     | 
| 312 | 
         
            +
                    if (i === index) {
         
     | 
| 313 | 
         
            +
                      layouts[i] = name
         
     | 
| 314 | 
         
            +
                    }
         
     | 
| 315 | 
         
            +
                  } else {
         
     | 
| 316 | 
         
            +
                    layouts[i] = name
         
     | 
| 317 | 
         
            +
                  }
         
     | 
| 318 | 
         
             
                }
         
     | 
| 319 | 
         | 
| 320 | 
         
             
                set({
         
     | 
| 
         | 
|
| 519 | 
         
             
              convertClapToComic: async (clap: ClapProject): Promise<{
         
     | 
| 520 | 
         
             
                currentNbPanels: number
         
     | 
| 521 | 
         
             
                prompt: string
         
     | 
| 522 | 
         
            +
                preset: Preset
         
     | 
| 523 | 
         
            +
                layout: LayoutName
         
     | 
| 524 | 
         
             
                storyPrompt: string
         
     | 
| 525 | 
         
             
                stylePrompt: string
         
     | 
| 526 | 
         
             
                panels: string[]
         
     | 
| 
         | 
|
| 585 | 
         
             
                  captions.push(ui.prompt)
         
     | 
| 586 | 
         
             
                })
         
     | 
| 587 | 
         | 
| 588 | 
         
            +
             
     | 
| 589 | 
         
            +
                return {
         
     | 
| 590 | 
         
             
                  currentNbPanels: shots.length,
         
     | 
| 591 | 
         
             
                  prompt,
         
     | 
| 592 | 
         
            +
                  preset: parsePresetFromPrompts(panels),
         
     | 
| 593 | 
         
            +
                  layout: await parseLayoutFromStoryboards(shots.map(x => x.storyboard)),
         
     | 
| 594 | 
         
             
                  storyPrompt,
         
     | 
| 595 | 
         
             
                  stylePrompt,
         
     | 
| 596 | 
         
             
                  panels,
         
     | 
| 
         | 
|
| 608 | 
         
             
                const {
         
     | 
| 609 | 
         
             
                  currentNbPanels,
         
     | 
| 610 | 
         
             
                  prompt,
         
     | 
| 611 | 
         
            +
                  preset,
         
     | 
| 612 | 
         
            +
                  layout,
         
     | 
| 613 | 
         
             
                  storyPrompt,
         
     | 
| 614 | 
         
             
                  stylePrompt,
         
     | 
| 615 | 
         
             
                  panels,
         
     | 
| 
         | 
|
| 625 | 
         
             
                  currentClap,
         
     | 
| 626 | 
         
             
                  currentNbPanels,
         
     | 
| 627 | 
         
             
                  prompt,
         
     | 
| 628 | 
         
            +
                  preset,
         
     | 
| 629 | 
         
            +
                  // layout,
         
     | 
| 630 | 
         
             
                  panels,
         
     | 
| 631 | 
         
             
                  renderedScenes,
         
     | 
| 632 | 
         
             
                  captions,
         
     | 
    	
        src/lib/getImageDimension.ts
    CHANGED
    
    | 
         @@ -1,16 +1,26 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 1 | 
         
             
            export interface ImageDimension {
         
     | 
| 2 | 
         
             
              width: number
         
     | 
| 3 | 
         
             
              height: number
         
     | 
| 
         | 
|
| 4 | 
         
             
            }
         
     | 
| 5 | 
         | 
| 6 | 
         
             
            export async function getImageDimension(src: string): Promise<ImageDimension> {
         
     | 
| 7 | 
         
             
              if (!src) {
         
     | 
| 8 | 
         
            -
                return { width: 0, height: 0 }
         
     | 
| 9 | 
         
             
              }
         
     | 
| 10 | 
         
             
              const img = new Image()
         
     | 
| 11 | 
         
             
              img.src = src
         
     | 
| 12 | 
         
             
              await img.decode()
         
     | 
| 13 | 
         
             
              const width = img.width
         
     | 
| 14 | 
         
             
              const height = img.height
         
     | 
| 15 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 16 | 
         
             
            }
         
     | 
| 
         | 
|
| 1 | 
         
            +
            import { ClapMediaOrientation } from "@aitube/clap"
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
             
            export interface ImageDimension {
         
     | 
| 4 | 
         
             
              width: number
         
     | 
| 5 | 
         
             
              height: number
         
     | 
| 6 | 
         
            +
              orientation: ClapMediaOrientation
         
     | 
| 7 | 
         
             
            }
         
     | 
| 8 | 
         | 
| 9 | 
         
             
            export async function getImageDimension(src: string): Promise<ImageDimension> {
         
     | 
| 10 | 
         
             
              if (!src) {
         
     | 
| 11 | 
         
            +
                return { width: 0, height: 0, orientation:  ClapMediaOrientation.SQUARE }
         
     | 
| 12 | 
         
             
              }
         
     | 
| 13 | 
         
             
              const img = new Image()
         
     | 
| 14 | 
         
             
              img.src = src
         
     | 
| 15 | 
         
             
              await img.decode()
         
     | 
| 16 | 
         
             
              const width = img.width
         
     | 
| 17 | 
         
             
              const height = img.height
         
     | 
| 18 | 
         
            +
             
     | 
| 19 | 
         
            +
              let orientation = ClapMediaOrientation.SQUARE
         
     | 
| 20 | 
         
            +
              if (width > height) { 
         
     | 
| 21 | 
         
            +
                orientation = ClapMediaOrientation.LANDSCAPE
         
     | 
| 22 | 
         
            +
              } else if (width < height) {
         
     | 
| 23 | 
         
            +
                orientation = ClapMediaOrientation.PORTRAIT
         
     | 
| 24 | 
         
            +
              }
         
     | 
| 25 | 
         
            +
              return { width, height, orientation }
         
     | 
| 26 | 
         
             
            }
         
     | 
    	
        src/lib/getOAuthRedirectUrl.ts
    ADDED
    
    | 
         @@ -0,0 +1,11 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            export function getOAuthRedirectUrl(): string {
         
     | 
| 2 | 
         
            +
              if (typeof window === "undefined") {
         
     | 
| 3 | 
         
            +
                return "http://localhost:3000"
         
     | 
| 4 | 
         
            +
              }
         
     | 
| 5 | 
         
            +
             
     | 
| 6 | 
         
            +
              return (
         
     | 
| 7 | 
         
            +
                window.location.hostname === "aicomicfactory.app" ? "https://aicomicfactory.app"
         
     | 
| 8 | 
         
            +
                : window.location.hostname === "jbilcke-hf-ai-comic-factory.hf.space" ? "https://jbilcke-hf-ai-comic-factory.hf.space"
         
     | 
| 9 | 
         
            +
                : "http://localhost:3000"
         
     | 
| 10 | 
         
            +
              )
         
     | 
| 11 | 
         
            +
            }
         
     | 
    	
        src/lib/isValidNumber.ts
    ADDED
    
    | 
         @@ -0,0 +1,3 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            export function isValidNumber(input?: any) {
         
     | 
| 2 | 
         
            +
              return typeof input === "number" && !isNaN(input) && isFinite(input)
         
     | 
| 3 | 
         
            +
            }
         
     | 
    	
        src/lib/parseLayoutFromStoryboards.ts
    ADDED
    
    | 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            import { ClapSegment } from "@aitube/clap"
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            import { LayoutName } from "@/app/layouts"
         
     | 
| 4 | 
         
            +
            import { layouts } from "@/app/layouts/settings"
         
     | 
| 5 | 
         
            +
            import { getImageDimension } from "./getImageDimension"
         
     | 
| 6 | 
         
            +
             
     | 
| 7 | 
         
            +
            export async function parseLayoutFromStoryboards(storyboards: ClapSegment[]): Promise<LayoutName> {
         
     | 
| 8 | 
         
            +
             
     | 
| 9 | 
         
            +
              let bestCandidate: LayoutName = "Layout0"
         
     | 
| 10 | 
         
            +
             
     | 
| 11 | 
         
            +
              for (const [layoutName, layoutPanels] of Object.entries(layouts)) {
         
     | 
| 12 | 
         
            +
             
     | 
| 13 | 
         
            +
                let nbMatchingStoryboards = 0
         
     | 
| 14 | 
         
            +
                let i = 0
         
     | 
| 15 | 
         
            +
             
     | 
| 16 | 
         
            +
                for (const { panel, orientation, width, height } of layoutPanels) {
         
     | 
| 17 | 
         
            +
             
     | 
| 18 | 
         
            +
                  const storyboard = storyboards[i]
         
     | 
| 19 | 
         
            +
             
     | 
| 20 | 
         
            +
                  if (!storyboard) { continue }
         
     | 
| 21 | 
         
            +
                  if (!storyboard?.assetUrl) { continue }
         
     | 
| 22 | 
         
            +
             
     | 
| 23 | 
         
            +
                  const imgDimension = await getImageDimension(storyboard.assetUrl)
         
     | 
| 24 | 
         
            +
             
     | 
| 25 | 
         
            +
                  if (orientation === imgDimension.orientation) {
         
     | 
| 26 | 
         
            +
                    nbMatchingStoryboards++
         
     | 
| 27 | 
         
            +
                  }
         
     | 
| 28 | 
         
            +
                  
         
     | 
| 29 | 
         
            +
                  i++
         
     | 
| 30 | 
         
            +
                }
         
     | 
| 31 | 
         
            +
             
     | 
| 32 | 
         
            +
                if (nbMatchingStoryboards === 4) {
         
     | 
| 33 | 
         
            +
                  bestCandidate = layoutName as LayoutName
         
     | 
| 34 | 
         
            +
                }
         
     | 
| 35 | 
         
            +
              }
         
     | 
| 36 | 
         
            +
             
     | 
| 37 | 
         
            +
              return bestCandidate
         
     | 
| 38 | 
         
            +
            }
         
     | 
    	
        src/lib/parsePresetFromPrompts.ts
    ADDED
    
    | 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            import { presets, PresetName, Preset } from "@/app/engine/presets"
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            /**
         
     | 
| 4 | 
         
            +
             * Try to guess the preset from a list of prompts
         
     | 
| 5 | 
         
            +
             * 
         
     | 
| 6 | 
         
            +
             * @param prompts 
         
     | 
| 7 | 
         
            +
             * @returns 
         
     | 
| 8 | 
         
            +
             */
         
     | 
| 9 | 
         
            +
            export function parsePresetFromPrompts(prompts: string[]): Preset {
         
     | 
| 10 | 
         
            +
             
     | 
| 11 | 
         
            +
              const presetToCount: Record<PresetName, number> = {}
         
     | 
| 12 | 
         
            +
              const chunkToPresets: Record<string, PresetName[]> = {}
         
     | 
| 13 | 
         
            +
              
         
     | 
| 14 | 
         
            +
              Object.values(presets).forEach(preset => {
         
     | 
| 15 | 
         
            +
                preset.imagePrompt("").map(x => x.trim().toLowerCase()).forEach(chunk => {
         
     | 
| 16 | 
         
            +
                  chunkToPresets[chunk] = Array.isArray(chunkToPresets[chunk]) ? chunkToPresets[chunk] : []
         
     | 
| 17 | 
         
            +
                  if (!chunkToPresets[chunk].includes(preset.id)) {
         
     | 
| 18 | 
         
            +
                    chunkToPresets[chunk].push(preset.id)
         
     | 
| 19 | 
         
            +
                  }
         
     | 
| 20 | 
         
            +
                })
         
     | 
| 21 | 
         
            +
              })
         
     | 
| 22 | 
         
            +
              
         
     | 
| 23 | 
         
            +
              prompts.forEach(prompt => {
         
     | 
| 24 | 
         
            +
                prompt.split(",").map(x => x.trim().toLowerCase()).forEach(chunk => {
         
     | 
| 25 | 
         
            +
                  if (Array.isArray(chunkToPresets[chunk])) {
         
     | 
| 26 | 
         
            +
                    const presetNames = chunkToPresets[chunk] as PresetName[]
         
     | 
| 27 | 
         
            +
                    presetNames.forEach(preset => {
         
     | 
| 28 | 
         
            +
                      presetToCount[preset] = (presetToCount[preset] || 0) + 1
         
     | 
| 29 | 
         
            +
                    })
         
     | 
| 30 | 
         
            +
                  }
         
     | 
| 31 | 
         
            +
                })
         
     | 
| 32 | 
         
            +
              })
         
     | 
| 33 | 
         
            +
             
     | 
| 34 | 
         
            +
              const bestMatch: PresetName | undefined = Object.entries(presetToCount).sort((a, b) => b[1] - a[1]).map(x => x[0]).at(0) as (PresetName | undefined)
         
     | 
| 35 | 
         
            +
             
     | 
| 36 | 
         
            +
              return presets[bestMatch || "neutral"] || presets.neutral
         
     | 
| 37 | 
         
            +
            }
         
     | 
    	
        src/lib/useImageDimension.ts
    CHANGED
    
    | 
         @@ -1,11 +1,13 @@ 
     | 
|
| 1 | 
         
             
            import { useEffect, useState } from "react"
         
     | 
| 2 | 
         | 
| 3 | 
         
             
            import { ImageDimension, getImageDimension } from "./getImageDimension"
         
     | 
| 
         | 
|
| 4 | 
         | 
| 5 | 
         
             
            export function useImageDimension(src: string) {
         
     | 
| 6 | 
         
             
              const [dimension, setDimension] = useState<ImageDimension>({
         
     | 
| 7 | 
         
             
                width: 0,
         
     | 
| 8 | 
         
             
                height: 0,
         
     | 
| 
         | 
|
| 9 | 
         
             
              })
         
     | 
| 10 | 
         | 
| 11 | 
         
             
              useEffect(() => {
         
     | 
| 
         | 
|
| 1 | 
         
             
            import { useEffect, useState } from "react"
         
     | 
| 2 | 
         | 
| 3 | 
         
             
            import { ImageDimension, getImageDimension } from "./getImageDimension"
         
     | 
| 4 | 
         
            +
            import { ClapMediaOrientation } from "@aitube/clap"
         
     | 
| 5 | 
         | 
| 6 | 
         
             
            export function useImageDimension(src: string) {
         
     | 
| 7 | 
         
             
              const [dimension, setDimension] = useState<ImageDimension>({
         
     | 
| 8 | 
         
             
                width: 0,
         
     | 
| 9 | 
         
             
                height: 0,
         
     | 
| 10 | 
         
            +
                orientation: ClapMediaOrientation.SQUARE
         
     | 
| 11 | 
         
             
              })
         
     | 
| 12 | 
         | 
| 13 | 
         
             
              useEffect(() => {
         
     | 
    	
        src/lib/useIsBusy.ts
    ADDED
    
    | 
         @@ -0,0 +1,9 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            import { useStore } from "@/app/store"
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            export function useIsBusy() {
         
     | 
| 4 | 
         
            +
              const isGeneratingStory = useStore(s => s.isGeneratingStory)
         
     | 
| 5 | 
         
            +
              const atLeastOnePanelIsBusy = useStore(s => s.atLeastOnePanelIsBusy)
         
     | 
| 6 | 
         
            +
              const isBusy = isGeneratingStory || atLeastOnePanelIsBusy
         
     | 
| 7 | 
         
            +
             
     | 
| 8 | 
         
            +
              return isBusy
         
     | 
| 9 | 
         
            +
            }
         
     | 
    	
        src/lib/useOAuth.ts
    CHANGED
    
    | 
         @@ -9,6 +9,7 @@ import { getValidOAuth } from "./getValidOAuth" 
     | 
|
| 9 | 
         
             
            import { useDynamicConfig } from "./useDynamicConfig"
         
     | 
| 10 | 
         
             
            import { useLocalStorage } from "usehooks-ts"
         
     | 
| 11 | 
         
             
            import { useShouldDisplayLoginWall } from "./useShouldDisplayLoginWall"
         
     | 
| 
         | 
|
| 12 | 
         | 
| 13 | 
         
             
            export function useOAuth({
         
     | 
| 14 | 
         
             
              debug = false
         
     | 
| 
         @@ -32,7 +33,10 @@ export function useOAuth({ 
     | 
|
| 32 | 
         
             
              const [oauthResult, setOAuthResult] = usePersistedOAuth()
         
     | 
| 33 | 
         | 
| 34 | 
         
             
              const clientId = config.oauthClientId
         
     | 
| 35 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 36 | 
         
             
              const scopes = config.oauthScopes
         
     | 
| 37 | 
         
             
              const enableOAuth = config.enableHuggingFaceOAuth
         
     | 
| 38 | 
         | 
| 
         | 
|
| 9 | 
         
             
            import { useDynamicConfig } from "./useDynamicConfig"
         
     | 
| 10 | 
         
             
            import { useLocalStorage } from "usehooks-ts"
         
     | 
| 11 | 
         
             
            import { useShouldDisplayLoginWall } from "./useShouldDisplayLoginWall"
         
     | 
| 12 | 
         
            +
            import { getOAuthRedirectUrl } from "./getOAuthRedirectUrl"
         
     | 
| 13 | 
         | 
| 14 | 
         
             
            export function useOAuth({
         
     | 
| 15 | 
         
             
              debug = false
         
     | 
| 
         | 
|
| 33 | 
         
             
              const [oauthResult, setOAuthResult] = usePersistedOAuth()
         
     | 
| 34 | 
         | 
| 35 | 
         
             
              const clientId = config.oauthClientId
         
     | 
| 36 | 
         
            +
             
     | 
| 37 | 
         
            +
              // const redirectUrl = config.oauthRedirectUrl
         
     | 
| 38 | 
         
            +
              const redirectUrl = getOAuthRedirectUrl()
         
     | 
| 39 | 
         
            +
             
     | 
| 40 | 
         
             
              const scopes = config.oauthScopes
         
     | 
| 41 | 
         
             
              const enableOAuth = config.enableHuggingFaceOAuth
         
     | 
| 42 | 
         |