plotly-cloud 0.1.0rc1__tar.gz → 0.1.0rc2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/.gitignore +5 -1
  2. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/PKG-INFO +3 -28
  3. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/README.md +2 -27
  4. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/_commands.py +8 -8
  5. plotly_cloud-0.1.0rc2/plotly_cloud/_devtool_hooks.py +61 -0
  6. plotly_cloud-0.1.0rc2/plotly_cloud/_devtool_publish_rpc.py +186 -0
  7. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/_oauth.py +69 -22
  8. plotly_cloud-0.1.0rc2/plotly_cloud/cloud_devtools.css +1 -0
  9. plotly_cloud-0.1.0rc2/plotly_cloud/cloud_devtools.js +15 -0
  10. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/pyproject.toml +5 -1
  11. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/LICENSE +0 -0
  12. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/__init__.py +0 -0
  13. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/_api_types.py +0 -0
  14. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/_changes.py +0 -0
  15. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/_cloud_env.py +0 -0
  16. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/_definitions.py +0 -0
  17. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/_deploy.py +0 -0
  18. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/_parser.py +0 -0
  19. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/cli.py +0 -0
  20. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/cloud-env.toml +0 -0
  21. {plotly_cloud-0.1.0rc1 → plotly_cloud-0.1.0rc2}/plotly_cloud/exceptions.py +0 -0
@@ -173,4 +173,8 @@ ehthumbs.db
173
173
  Thumbs.db
174
174
 
175
175
  # Cloud environment configuration (created at packaging time)
176
- cloud-env.toml
176
+ cloud-env.toml
177
+
178
+ # React component build output
179
+ plotly_cloud/cloud_devtools.js*
180
+ plotly_cloud/cloud_devtools.css*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotly-cloud
3
- Version: 0.1.0rc1
3
+ Version: 0.1.0rc2
4
4
  Summary: CLI for interacting with Plotly Cloud to deploy Dash apps
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.9
@@ -26,33 +26,8 @@ A command-line interface for interacting with Plotly Cloud to publish and manage
26
26
 
27
27
  ## Installation
28
28
 
29
- ### From Source
30
-
31
- ```bash
32
- git clone <repository-url>
33
- cd plotly-cloud-cli
34
- pip install -e .
35
- ```
36
-
37
- ### Development Installation
38
-
39
- For development, we recommend using UV and the provided Justfile:
40
-
41
29
  ```bash
42
- git clone <repository-url>
43
- cd plotly-cloud-cli
44
-
45
- # Install UV if you haven't already
46
- curl -LsSf https://astral.sh/uv/install.sh | sh
47
-
48
- # Install dependencies
49
- just install
50
-
51
- # Setup cloud environment configuration
52
- just setup-cloud-env "your-staging-client-id" "your-production-client-id"
53
-
54
- # Install CLI in development mode
55
- just install-cli
30
+ pip install plotly-cloud
56
31
  ```
57
32
 
58
33
  ## Quick Start
@@ -313,7 +288,7 @@ plotly-cloud-cli/
313
288
 
314
289
  ## License
315
290
 
316
- [License information to be added]
291
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
317
292
 
318
293
  ## Support
319
294
 
@@ -11,33 +11,8 @@ A command-line interface for interacting with Plotly Cloud to publish and manage
11
11
 
12
12
  ## Installation
13
13
 
14
- ### From Source
15
-
16
- ```bash
17
- git clone <repository-url>
18
- cd plotly-cloud-cli
19
- pip install -e .
20
- ```
21
-
22
- ### Development Installation
23
-
24
- For development, we recommend using UV and the provided Justfile:
25
-
26
14
  ```bash
27
- git clone <repository-url>
28
- cd plotly-cloud-cli
29
-
30
- # Install UV if you haven't already
31
- curl -LsSf https://astral.sh/uv/install.sh | sh
32
-
33
- # Install dependencies
34
- just install
35
-
36
- # Setup cloud environment configuration
37
- just setup-cloud-env "your-staging-client-id" "your-production-client-id"
38
-
39
- # Install CLI in development mode
40
- just install-cli
15
+ pip install plotly-cloud
41
16
  ```
42
17
 
43
18
  ## Quick Start
@@ -298,7 +273,7 @@ plotly-cloud-cli/
298
273
 
299
274
  ## License
300
275
 
301
- [License information to be added]
276
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
302
277
 
303
278
  ## Support
304
279
 
@@ -428,43 +428,43 @@ class PublishCommand(BaseCommand):
428
428
  {
429
429
  "name": "--project-path",
430
430
  "default": ".",
431
- "help": "Path to project directory to deploy (default: current directory)",
431
+ "help": "Path to project directory to publish (default: current directory)",
432
432
  },
433
433
  {
434
434
  "name": "--config",
435
435
  "default": "plotly-cloud.toml",
436
- "help": "Path to configuration file (default: plotly-cloud.toml)",
436
+ "help": "Path to configuration file",
437
437
  },
438
438
  {
439
439
  "name": "--name",
440
- "help": "Application name (will prompt if not provided for first deployment)",
440
+ "help": "Application name (will prompt if not provided first time app is published)",
441
441
  },
442
442
  {
443
443
  "name": "--output",
444
- "help": "Output path for deployment zip file (default: temporary file)",
444
+ "help": "Output path for zip file of the published app (default: temporary file)",
445
445
  },
446
446
  {
447
447
  "name": "--keep-zip",
448
448
  "action": "store_true",
449
- "help": "Keep the deployment zip file after upload",
449
+ "help": "Keep the zip file of the published app after upload",
450
450
  },
451
451
  {
452
452
  "name": "--poll-status",
453
453
  "type": lambda x: x.lower() in ("true", "1", "yes", "on"), # type: ignore
454
454
  "default": True,
455
- "help": "Poll deployment status until completion (default: True)",
455
+ "help": "Poll publishing status until completion (default: True)",
456
456
  },
457
457
  {
458
458
  "name": "--poll-interval",
459
459
  "type": float,
460
460
  "default": 1.0,
461
- "help": "Polling interval in seconds (default: 1.0)",
461
+ "help": "Polling interval in seconds",
462
462
  },
463
463
  {
464
464
  "name": "--poll-timeout",
465
465
  "type": int,
466
466
  "default": 180,
467
- "help": "Polling timeout in seconds (default: 180 = 3 minutes)",
467
+ "help": "Polling timeout in seconds",
468
468
  },
469
469
  ]
470
470
 
@@ -0,0 +1,61 @@
1
+ import asyncio
2
+
3
+ import dash
4
+ import flask
5
+ from packaging.version import parse as _parse_version
6
+
7
+ from plotly_cloud._devtool_publish_rpc import PlotlyCloudPublishRPC
8
+
9
+ dash_version = _parse_version(dash.__version__)
10
+
11
+
12
+ def _run_sync(coro):
13
+ loop = asyncio.get_event_loop()
14
+ return loop.run_until_complete(coro)
15
+
16
+
17
+ def install_hook():
18
+ import nest_asyncio
19
+
20
+ # Make asyncio stuff runs smoothly.
21
+ # Prevent no loop and existing loop errors.
22
+ nest_asyncio.apply()
23
+
24
+ rpc = PlotlyCloudPublishRPC()
25
+
26
+ try:
27
+ # The style only works with the position left defined.
28
+ dash.hooks.devtool( # type: ignore
29
+ "plotly_cloud_publish_component",
30
+ "PlotlyCloudPublishComponent",
31
+ {"id": "_plotly-cloud-publish"},
32
+ position="left",
33
+ )
34
+ except Exception:
35
+ return
36
+
37
+ dash.hooks.script(
38
+ [
39
+ {"dev_package_path": "cloud_devtools.js", "namespace": "plotly_cloud", "dev_only": True},
40
+ ]
41
+ )
42
+
43
+ dash.hooks.stylesheet(
44
+ [
45
+ {"relative_package_path": "cloud_devtools.css", "namespace": "plotly_cloud"},
46
+ ]
47
+ )
48
+
49
+ @dash.hooks.route("_plotly_cloud_publish", methods=["POST"])
50
+ def plotly_cloud_publish_rpc():
51
+ data = flask.request.get_json()
52
+ data = _run_sync(rpc.handle_operation(data))
53
+ return flask.jsonify(data)
54
+
55
+ @dash.hooks.setup()
56
+ def plotly_cloud_setup(app):
57
+ rpc._app_setup = app
58
+
59
+
60
+ if hasattr(dash.hooks, "devtool"):
61
+ install_hook()
@@ -0,0 +1,186 @@
1
+ """RPC interface for Plotly Cloud publishing in dev tools."""
2
+
3
+ import importlib
4
+ import os
5
+ import tempfile
6
+ import traceback
7
+ from typing import Any
8
+
9
+ import dash
10
+ from typing_extensions import Literal, NotRequired, TypedDict
11
+
12
+ from plotly_cloud._cloud_env import cloud_config
13
+ from plotly_cloud._definitions import AppDeploymentConfig
14
+ from plotly_cloud._deploy import (
15
+ DeploymentClient,
16
+ create_deployment_zip,
17
+ get_config_path,
18
+ load_deployment_config,
19
+ save_deployment_config,
20
+ validate_dependencies,
21
+ )
22
+ from plotly_cloud._oauth import OAuthClient
23
+
24
+
25
+ class PublishOperations:
26
+ check_auth = "initialize"
27
+
28
+
29
+ class PublishOperation(TypedDict):
30
+ """RPC operation structure for dev tools publishing."""
31
+
32
+ operation: Literal["initialize", "authenticate", "auth_poll", "publish", "status"]
33
+ data: Any
34
+
35
+
36
+ class RPCResponse(TypedDict):
37
+ """Standard RPC response structure."""
38
+
39
+ result: NotRequired[Any]
40
+ error: NotRequired[str]
41
+
42
+
43
+ class PlotlyCloudPublishRPC:
44
+ """RPC handler for Plotly Cloud publishing operations in dev tools."""
45
+
46
+ def __init__(self) -> None:
47
+ """Initialize the RPC handler."""
48
+ client_id = cloud_config.get_oauth_client_id()
49
+ self.oauth_client = OAuthClient(client_id)
50
+ self._app_setup = None # Fallback incase get_app fails.
51
+
52
+ def get_project_path(self):
53
+ app = None
54
+ try:
55
+ app = dash.det_app() # type: ignore
56
+ except Exception:
57
+ app = self._app_setup
58
+
59
+ assert app
60
+ app_module = importlib.import_module(app.config.name)
61
+ return os.path.dirname(str(app_module.__file__))
62
+
63
+ async def handle_operation(self, publish_operation: PublishOperation) -> RPCResponse:
64
+ """Handle a publish operation from dev tools.
65
+
66
+ Args:
67
+ publish_operation: The operation to perform with its data
68
+
69
+ Returns:
70
+ RPCResponse with data and optional error
71
+
72
+ Raises:
73
+ ValueError: If operation is not supported
74
+ """
75
+ operation_name = publish_operation["operation"]
76
+ data = publish_operation.get("data")
77
+
78
+ # Get the method by operation name (direct match)
79
+ if not hasattr(self, operation_name):
80
+ raise ValueError(f"Unsupported operation: {operation_name}")
81
+
82
+ try:
83
+ method = getattr(self, operation_name)
84
+ return await method(data)
85
+ except Exception as e:
86
+ traceback.print_last()
87
+ return {"error": str(e)}
88
+
89
+ async def initialize(self, data: Any) -> RPCResponse:
90
+ is_authenticated = await self.oauth_client.is_authenticated()
91
+
92
+ project_path = self.get_project_path()
93
+
94
+ config_path = get_config_path(project_path, os.path.join(project_path, "plotly-cloud.toml"))
95
+ config = load_deployment_config(config_path)
96
+
97
+ app_id = config.get("app_id")
98
+ existing = app_id is not None
99
+ status = "new"
100
+ app_name = config.get("name", os.path.basename(project_path))
101
+
102
+ if app_id is not None and is_authenticated:
103
+ async with DeploymentClient(self.oauth_client) as deploy_client:
104
+ status_data = await deploy_client.get_app_status(app_id)
105
+ status = status_data.get("status", "")
106
+
107
+ return {
108
+ "result": {
109
+ "authenticated": is_authenticated,
110
+ "existing": existing,
111
+ "status": status,
112
+ "app_name": app_name,
113
+ "app_path": project_path,
114
+ "app_id": app_id,
115
+ }
116
+ }
117
+
118
+ async def authenticate(self, data: Any) -> RPCResponse:
119
+ device_auth = await self.oauth_client.request_device_authorization()
120
+ return {"result": device_auth}
121
+
122
+ async def auth_poll(self, data: Any) -> RPCResponse:
123
+ device_code = data.get("device_code")
124
+ status_code, response = await self.oauth_client.check_authentication_status(device_code)
125
+ if status_code == 200:
126
+ await self.oauth_client._save_credentials(dict(response))
127
+ return {"result": {"success": True}}
128
+ else:
129
+ error = response.get("error", "unknown_error")
130
+ if error == "authorization_pending":
131
+ return {"result": {}}
132
+ elif error == "slow_down":
133
+ delay = 1 + data.get("delayed", 0)
134
+ return {"result": {"delay": delay}}
135
+ elif error == "expired_token":
136
+ return {"result": {"try_again": True}}
137
+ elif error == "access_denied":
138
+ return {"error": "Access denied by user"}
139
+ else:
140
+ return {"error": "Authentication Failed"}
141
+
142
+ async def status(self, data: Any) -> RPCResponse:
143
+ app_id = data.get("app_id")
144
+ async with DeploymentClient(self.oauth_client) as deploy_client:
145
+ status_data = await deploy_client.get_app_status(app_id)
146
+ return {"result": {"status": status_data.get("status", "")}}
147
+
148
+ async def publish(self, data: Any) -> RPCResponse:
149
+ app_path = data.get("app_path")
150
+ app_id = data.get("app_id")
151
+ app_name = data.get("app_name")
152
+
153
+ config_path = get_config_path(app_path)
154
+
155
+ temp_file = tempfile.NamedTemporaryFile(suffix=".zip", delete=False)
156
+ zip_path = temp_file.name
157
+ temp_file.close()
158
+
159
+ validate_dependencies(app_path)
160
+
161
+ await create_deployment_zip(app_path, zip_path)
162
+
163
+ async with DeploymentClient(self.oauth_client) as deploy_client:
164
+ if app_id:
165
+ # update app
166
+ app_data = await deploy_client.publish_app(app_id, zip_path)
167
+ config = load_deployment_config(config_path)
168
+
169
+ if config.get("app_url") != app_data.get("app_url"):
170
+ config["app_url"] = app_data.get("app_url", "")
171
+ save_deployment_config(config, config_path)
172
+
173
+ return {"result": {"app_id": app_id}}
174
+ else:
175
+ # create new app
176
+ app_data = await deploy_client.create_app(app_name, zip_path)
177
+
178
+ config: AppDeploymentConfig = {
179
+ "name": app_name,
180
+ "app_id": app_data.get("id", ""),
181
+ "app_url": app_data.get("app_url", ""),
182
+ }
183
+
184
+ save_deployment_config(config, config_path)
185
+
186
+ return {"result": {"app_id": app_data.get("id")}}
@@ -6,13 +6,14 @@ import os
6
6
  import time
7
7
  import webbrowser
8
8
  from pathlib import Path
9
- from typing import Optional
9
+ from typing import Any, Dict, Optional, Tuple, Union
10
10
 
11
11
  import httpx
12
12
  from rich.console import Console
13
13
  from rich.live import Live
14
14
  from rich.panel import Panel
15
15
  from rich.spinner import Spinner
16
+ from typing_extensions import TypedDict
16
17
 
17
18
  from .exceptions import (
18
19
  CredentialError,
@@ -36,6 +37,25 @@ WORKOS_ENDPOINTS = {
36
37
  DEFAULT_AUTH_PROVIDER = "authkit"
37
38
 
38
39
 
40
+ class AuthTokenResponse(TypedDict):
41
+ """Response from successful OAuth authentication."""
42
+
43
+ access_token: str
44
+ refresh_token: str
45
+ token_type: str
46
+ expires_in: int
47
+
48
+
49
+ class AuthErrorResponse(TypedDict):
50
+ """Response from failed OAuth authentication."""
51
+
52
+ error: str
53
+ error_description: Optional[str]
54
+
55
+
56
+ AuthResponse = Union[AuthTokenResponse, AuthErrorResponse]
57
+
58
+
39
59
  class OAuthClient:
40
60
  """OAuth client for WorkOS CLI Auth using device authorization flow."""
41
61
 
@@ -70,7 +90,45 @@ class OAuthClient:
70
90
 
71
91
  return response.json()
72
92
 
73
- async def poll_for_authentication(self, device_code: str, interval: int = 5, timeout: int = 300) -> dict:
93
+ async def check_authentication_status(
94
+ self,
95
+ device_code: str,
96
+ client: Optional[httpx.AsyncClient] = None,
97
+ ) -> Tuple[int, AuthResponse]:
98
+ """Check authentication status without terminal output or polling loop.
99
+
100
+ Args:
101
+ device_code: The device code from device authorization
102
+ client: Optional httpx client to use, creates new one if not provided
103
+
104
+ Returns:
105
+ Tuple of (status_code, response_data)
106
+ """
107
+ token_data = {
108
+ "client_id": self.client_id,
109
+ "device_code": device_code,
110
+ "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
111
+ }
112
+
113
+ if client:
114
+ response = await client.post(
115
+ f"{WORKOS_API_BASE_URL}{WORKOS_ENDPOINTS['AUTHENTICATE']}",
116
+ data=token_data,
117
+ headers={"Content-Type": "application/x-www-form-urlencoded", "user-agent": "PlotlyCloudCLI"},
118
+ )
119
+ else:
120
+ async with httpx.AsyncClient() as new_client:
121
+ response = await new_client.post(
122
+ f"{WORKOS_API_BASE_URL}{WORKOS_ENDPOINTS['AUTHENTICATE']}",
123
+ data=token_data,
124
+ headers={"Content-Type": "application/x-www-form-urlencoded", "user-agent": "PlotlyCloudCLI"},
125
+ )
126
+
127
+ return response.status_code, response.json()
128
+
129
+ async def poll_for_authentication(
130
+ self, device_code: str, interval: int = 5, timeout: int = 300
131
+ ) -> AuthTokenResponse:
74
132
  """Poll for authentication completion with exponential backoff."""
75
133
  start_time = time.time()
76
134
  current_interval = interval
@@ -79,24 +137,13 @@ class OAuthClient:
79
137
  spinner = Spinner("dots", text="⏳ Waiting for authorization...")
80
138
 
81
139
  with Live(spinner, console=console, refresh_per_second=4):
82
- while time.time() - start_time < timeout:
83
- token_data = {
84
- "client_id": self.client_id,
85
- "device_code": device_code,
86
- "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
87
- }
88
-
89
- async with httpx.AsyncClient() as client:
90
- response = await client.post(
91
- f"{WORKOS_API_BASE_URL}{WORKOS_ENDPOINTS['AUTHENTICATE']}",
92
- data=token_data,
93
- headers={"Content-Type": "application/x-www-form-urlencoded", "user-agent": "PlotlyCloudCLI"},
94
- )
140
+ async with httpx.AsyncClient() as client:
141
+ while time.time() - start_time < timeout:
142
+ status_code, response_data = await self.check_authentication_status(device_code, client)
95
143
 
96
- if response.status_code == 200:
97
- return response.json()
144
+ if status_code == 200:
145
+ return response_data # type: ignore
98
146
 
99
- response_data = response.json()
100
147
  error = response_data.get("error", "unknown_error")
101
148
 
102
149
  if error == "authorization_pending":
@@ -121,7 +168,7 @@ class OAuthClient:
121
168
 
122
169
  raise TimeoutError("Authentication timed out. Please try again.")
123
170
 
124
- async def login(self, open_browser: bool = True, provider: str = DEFAULT_AUTH_PROVIDER) -> dict:
171
+ async def login(self, open_browser: bool = True, provider: str = DEFAULT_AUTH_PROVIDER) -> AuthTokenResponse:
125
172
  """Perform CLI Auth device flow login."""
126
173
 
127
174
  # Step 1: Request device authorization
@@ -167,11 +214,11 @@ class OAuthClient:
167
214
  raise
168
215
 
169
216
  # Step 4: Save credentials
170
- await self._save_credentials(tokens)
217
+ await self._save_credentials(dict(tokens))
171
218
 
172
219
  return tokens
173
220
 
174
- async def _save_credentials(self, credentials: dict):
221
+ async def _save_credentials(self, credentials: Dict[str, Any]):
175
222
  """Save credentials to file."""
176
223
  try:
177
224
  # Ensure parent directory exists
@@ -187,7 +234,7 @@ class OAuthClient:
187
234
  except Exception as e:
188
235
  raise CredentialError("Failed to save credentials", str(e)) from e
189
236
 
190
- async def load_credentials(self) -> Optional[dict]:
237
+ async def load_credentials(self) -> Optional[Dict[str, Any]]:
191
238
  """Load saved credentials."""
192
239
  try:
193
240
  if not self.credentials_path.exists():
@@ -0,0 +1 @@
1
+ .plotly-cloud-publish-container{position:relative;font-family:Verdana,Geneva,sans-serif}.plotly-cloud-publish-button{cursor:pointer}.plotly-cloud-publish-button:hover{color:#7f4bc4}.plotly-cloud-publish-modal-overlay{position:absolute;bottom:100%;left:-16px;z-index:10000;margin-bottom:1.25rem}.plotly-cloud-publish-modal-content{width:522px;background:#fff;border:1px solid #d1d5db;border-radius:4px 4px 0 0;box-shadow:0 -4px 6px #00000014;overflow:hidden}.plotly-cloud-publish-modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid #e5e5e5;background:#f9fafb}.plotly-cloud-publish-modal-header h3{margin:0;color:#333}.plotly-cloud-publish-modal-close{background:none;border:none;font-size:24px;cursor:pointer;color:#666;padding:0;width:30px;height:30px;display:flex;align-items:center;justify-content:center}.plotly-cloud-publish-modal-close:hover{color:#333}.plotly-cloud-publish-modal-body{padding:20px;color:#000!important;font-weight:100}button.plotly-cloud-publish-modal-button{display:inline-flex;align-items:center;gap:6px;padding:6px 10px!important;border-radius:4px!important;font-size:12px!important;font-weight:600!important;cursor:pointer;border:1px solid transparent;border-top-color:transparent;border-right-color:transparent;border-bottom-color:transparent;border-left-color:transparent;background:#f3f4f6;color:#374151}.plotly-cloud-publish-modal-button:disabled{cursor:not-allowed}button.plotly-cloud-btn-primary{background:#8b5cf6!important;color:#fff;border-color:#7c3aed!important}button.plotly-cloud-btn-secondary{background:#fff;border:none;color:#8b5cf6!important}.plotly-cloud-button-bar{display:flex;justify-content:flex-end;align-items:center}.plotly-cloud-publish-signin{display:flex;justify-content:space-between;align-items:center}.plotly-cloud-name-input-label{font-weight:600;color:#374151;font-size:12px;margin-bottom:6px}.plotly-cloud-name-input-field{margin-bottom:12px;width:calc(100% - 8px)}.plotly-cloud-status-info{display:flex;border-radius:5px;margin-bottom:12px;align-items:center;padding:6px;font-size:12px!important}.plotly-cloud-status-red{background:#fef2f2;color:#991b1b;border:1px solid #fecaca}.plotly-cloud-status-description{font-size:12px!important;margin-left:8px}.plotly-cloud-status-title{font-weight:700}.plotly-cloud-status-green{background:#ecfdf5;color:#065f46;border:1px solid #a7f3d0}.plotly-cloud-status-red .plotly-cloud-status-title{color:#a10}.plotly-cloud-status-green .plotly-cloud-status-title{color:#065f46}.plotly-cloud-status-gray{background:#f9fafb;color:#000;border:1px solid #dadada}.plotly-cloud-status-gray .plotly-cloud-status-title{color:#2e2e2e}.plotly-cloud-green-icon{color:#10b981!important}.plotly-cloud-red-icon{color:#ef4444}.plotly-cloud-gray-icon{color:#9ca3af}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.plotly-cloud-spin{animation:spin 2s linear infinite}
@@ -0,0 +1,15 @@
1
+ const React = window.React; const ReactDOM = window.ReactDOM;
2
+ "use strict";var plotly_cloud_publish_component=(()=>{var Go=Object.create;var G=Object.defineProperty;var Oo=Object.getOwnPropertyDescriptor;var Wo=Object.getOwnPropertyNames;var qo=Object.getPrototypeOf,Vo=Object.prototype.hasOwnProperty;var ao=(o,a)=>()=>(a||o((a={exports:{}}).exports,a),a.exports),zo=(o,a)=>{for(var s in a)G(o,s,{get:a[s],enumerable:!0})},mo=(o,a,s,r)=>{if(a&&typeof a=="object"||typeof a=="function")for(let n of Wo(a))!Vo.call(o,n)&&n!==s&&G(o,n,{get:()=>a[n],enumerable:!(r=Oo(a,n))||r.enumerable});return o};var e=(o,a,s)=>(s=o!=null?Go(qo(o)):{},mo(a||!o||!o.__esModule?G(s,"default",{value:o,enumerable:!0}):s,o)),Xo=o=>mo(G({},"__esModule",{value:!0}),o);var u=ao((fa,po)=>{po.exports=React});var fo=ao(O=>{"use strict";var _o=u(),Zo=Symbol.for("react.element"),Jo=Symbol.for("react.fragment"),Ko=Object.prototype.hasOwnProperty,jo=_o.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,Qo={key:!0,ref:!0,__self:!0,__source:!0};function Io(o,a,s){var r,n={},I=null,h=null;s!==void 0&&(I=""+s),a.key!==void 0&&(I=""+a.key),a.ref!==void 0&&(h=a.ref);for(r in a)Ko.call(a,r)&&!Qo.hasOwnProperty(r)&&(n[r]=a[r]);if(o&&o.defaultProps)for(r in a=o.defaultProps,a)n[r]===void 0&&(n[r]=a[r]);return{$$typeof:Zo,type:o,key:I,ref:h,props:n,_owner:jo.current}}O.Fragment=Jo;O.jsx=Io;O.jsxs=Io});var S=ao((Sa,uo)=>{"use strict";uo.exports=fo()});var ia={};zo(ia,{PlotlyCloudPublishComponent:()=>ro});var t=e(u());var k=e(S()),Yo=({title:o,onClose:a,children:s})=>(0,k.jsx)("div",{className:"plotly-cloud-publish-modal-overlay",children:(0,k.jsxs)("div",{className:"plotly-cloud-publish-modal-content",children:[(0,k.jsxs)("div",{className:"plotly-cloud-publish-modal-header",children:[(0,k.jsx)("h3",{children:o},"modal-title"),(0,k.jsx)("button",{className:"plotly-cloud-publish-modal-close",onClick:a,children:"\xD7"},"modal-close")]}),(0,k.jsx)("div",{className:"plotly-cloud-publish-modal-body",children:s},"modal-body")]})}),So=Yo;var go=e(u()),L=e(S()),$o=o=>{let[a,s]=(0,go.useState)(o.waiting);return(0,L.jsx)("div",{className:"plotly-cloud-publish-signin",children:a?(0,L.jsx)("span",{children:"Waiting for succesful sign-in on signin.cloud.plotly.com"}):(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)("span",{children:"Sign in to publish your app to Plotly Cloud."}),(0,L.jsx)("button",{className:"plotly-cloud-publish-modal-button plotly-cloud-btn-primary",onClick:()=>{s(!0),o.onSignIn()},children:"Sign In"})]})})},ho=$o;var oa=o=>{let a=async r=>await(await fetch(`${o}_plotly_cloud_publish`,{method:"POST",body:JSON.stringify(r),headers:{"Content-Type":"application/json"}})).json(),s=r=>n=>a({operation:r,data:n});return{initialize:s("initialize"),authenticate:s("authenticate"),auth_poll:s("auth_poll"),publish:s("publish"),status:s("status")}},Co=oa;var wo=e(u()),b=e(S()),aa=({initialAppName:o="",onCancel:a,onPublish:s})=>{let[r,n]=(0,wo.useState)(o);return(0,b.jsxs)("div",{className:"plotly-cloud-name-input",children:[(0,b.jsx)("div",{className:"plotly-cloud-name-input-label",children:"App Name"}),(0,b.jsxs)("form",{onSubmit:h=>{h.preventDefault(),r.trim()&&s(r.trim())},children:[(0,b.jsx)("input",{type:"text",value:r,onChange:h=>n(h.target.value),placeholder:"My Dash App",className:"plotly-cloud-name-input-field"}),(0,b.jsxs)("div",{className:"plotly-cloud-button-bar",children:[(0,b.jsx)("button",{type:"button",onClick:a,className:"plotly-cloud-publish-modal-button plotly-cloud-btn-secondary",children:"Cancel"}),(0,b.jsx)("button",{type:"submit",disabled:!r.trim(),className:"plotly-cloud-publish-modal-button plotly-cloud-btn-primary",children:"Publish App"})]})]})]})},Lo=aa;var Q=e(u());var c=e(u(),1),bo=new Map([["bold",c.createElement(c.Fragment,null,c.createElement("path",{d:"M236,128a108,108,0,0,1-216,0c0-42.52,24.73-81.34,63-98.9A12,12,0,1,1,93,50.91C63.24,64.57,44,94.83,44,128a84,84,0,0,0,168,0c0-33.17-19.24-63.43-49-77.09A12,12,0,1,1,173,29.1C211.27,46.66,236,85.48,236,128Z"}))],["duotone",c.createElement(c.Fragment,null,c.createElement("path",{d:"M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z",opacity:"0.2"}),c.createElement("path",{d:"M232,128a104,104,0,0,1-208,0c0-41,23.81-78.36,60.66-95.27a8,8,0,0,1,6.68,14.54C60.15,61.59,40,93.27,40,128a88,88,0,0,0,176,0c0-34.73-20.15-66.41-51.34-80.73a8,8,0,0,1,6.68-14.54C208.19,49.64,232,87,232,128Z"}))],["fill",c.createElement(c.Fragment,null,c.createElement("path",{d:"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,176A72,72,0,0,1,92,65.64a8,8,0,0,1,8,13.85,56,56,0,1,0,56,0,8,8,0,0,1,8-13.85A72,72,0,0,1,128,200Z"}))],["light",c.createElement(c.Fragment,null,c.createElement("path",{d:"M230,128a102,102,0,0,1-204,0c0-40.18,23.35-76.86,59.5-93.45a6,6,0,0,1,5,10.9C58.61,60.09,38,92.49,38,128a90,90,0,0,0,180,0c0-35.51-20.61-67.91-52.5-82.55a6,6,0,0,1,5-10.9C206.65,51.14,230,87.82,230,128Z"}))],["regular",c.createElement(c.Fragment,null,c.createElement("path",{d:"M232,128a104,104,0,0,1-208,0c0-41,23.81-78.36,60.66-95.27a8,8,0,0,1,6.68,14.54C60.15,61.59,40,93.27,40,128a88,88,0,0,0,176,0c0-34.73-20.15-66.41-51.34-80.73a8,8,0,0,1,6.68-14.54C208.19,49.64,232,87,232,128Z"}))],["thin",c.createElement(c.Fragment,null,c.createElement("path",{d:"M228,128a100,100,0,0,1-200,0c0-39.4,22.9-75.37,58.33-91.63a4,4,0,1,1,3.34,7.27C57.07,58.6,36,91.71,36,128a92,92,0,0,0,184,0c0-36.29-21.07-69.4-53.67-84.36a4,4,0,1,1,3.34-7.27C205.1,52.63,228,88.6,228,128Z"}))]]);var l=e(u(),1),Po=new Map([["bold",l.createElement(l.Fragment,null,l.createElement("path",{d:"M160,36A92.09,92.09,0,0,0,79,84.36,68,68,0,1,0,72,220h88a92,92,0,0,0,0-184Zm0,160H72a44,44,0,0,1-1.82-88A91.86,91.86,0,0,0,68,128a12,12,0,0,0,24,0,68,68,0,1,1,68,68Zm40.49-92.49a12,12,0,0,1,0,17l-48,48a12,12,0,0,1-17,0l-24-24a12,12,0,1,1,17-17L144,143l39.51-39.52A12,12,0,0,1,200.49,103.51Z"}))],["duotone",l.createElement(l.Fragment,null,l.createElement("path",{d:"M240,128a80,80,0,0,1-80,80H72A56,56,0,1,1,85.92,97.74l0,.1A80,80,0,0,1,240,128Z",opacity:"0.2"}),l.createElement("path",{d:"M160,40A88.09,88.09,0,0,0,81.29,88.67,64,64,0,1,0,72,216h88a88,88,0,0,0,0-176Zm0,160H72a48,48,0,0,1,0-96c1.1,0,2.2,0,3.29.11A88,88,0,0,0,72,128a8,8,0,0,0,16,0,72,72,0,1,1,72,72Zm37.66-93.66a8,8,0,0,1,0,11.32l-48,48a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L144,148.69l42.34-42.35A8,8,0,0,1,197.66,106.34Z"}))],["fill",l.createElement(l.Fragment,null,l.createElement("path",{d:"M247.93,124.52C246.11,77.54,207.07,40,160.06,40A88.1,88.1,0,0,0,81.29,88.67h0A87.48,87.48,0,0,0,72,127.73,8.18,8.18,0,0,1,64.57,136,8,8,0,0,1,56,128a103.66,103.66,0,0,1,5.34-32.92,4,4,0,0,0-4.75-5.18A64.09,64.09,0,0,0,8,152c0,35.19,29.75,64,65,64H160A88.09,88.09,0,0,0,247.93,124.52Zm-50.27-6.86-48,48a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L144,148.69l42.34-42.35a8,8,0,0,1,11.32,11.32Z"}))],["light",l.createElement(l.Fragment,null,l.createElement("path",{d:"M160,42A86.11,86.11,0,0,0,82.43,90.88,62,62,0,1,0,72,214h88a86,86,0,0,0,0-172Zm0,160H72a50,50,0,0,1,0-100,50.67,50.67,0,0,1,5.91.35A85.61,85.61,0,0,0,74,128a6,6,0,0,0,12,0,74,74,0,1,1,74,74Zm36.24-94.24a6,6,0,0,1,0,8.48l-48,48a6,6,0,0,1-8.48,0l-24-24a6,6,0,0,1,8.48-8.48L144,151.51l43.76-43.75A6,6,0,0,1,196.24,107.76Z"}))],["regular",l.createElement(l.Fragment,null,l.createElement("path",{d:"M160,40A88.09,88.09,0,0,0,81.29,88.67,64,64,0,1,0,72,216h88a88,88,0,0,0,0-176Zm0,160H72a48,48,0,0,1,0-96c1.1,0,2.2,0,3.29.11A88,88,0,0,0,72,128a8,8,0,0,0,16,0,72,72,0,1,1,72,72Zm37.66-93.66a8,8,0,0,1,0,11.32l-48,48a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L144,148.69l42.34-42.35A8,8,0,0,1,197.66,106.34Z"}))],["thin",l.createElement(l.Fragment,null,l.createElement("path",{d:"M160,44A84.11,84.11,0,0,0,83.59,93.12,60.71,60.71,0,0,0,72,92a60,60,0,0,0,0,120h88a84,84,0,0,0,0-168Zm0,160H72a52,52,0,1,1,8.55-103.3A83.66,83.66,0,0,0,76,128a4,4,0,0,0,8,0,76,76,0,1,1,76,76Zm34.83-94.83a4,4,0,0,1,0,5.66l-48,48a4,4,0,0,1-5.66,0l-24-24a4,4,0,0,1,5.66-5.66L144,154.34l45.17-45.17A4,4,0,0,1,194.83,109.17Z"}))]]);var m=e(u(),1),Ao=new Map([["bold",m.createElement(m.Fragment,null,m.createElement("path",{d:"M56.88,31.93A12,12,0,1,0,39.12,48.07L71.79,84A68,68,0,0,0,72,220h88a91.26,91.26,0,0,0,30.66-5.24l8.46,9.31a12,12,0,0,0,17.76-16.14ZM160,196H72a44,44,0,0,1-1.8-87.95A91.91,91.91,0,0,0,68,128a12,12,0,0,0,24,0,68.22,68.22,0,0,1,2.66-18.84l77.88,85.67A68.67,68.67,0,0,1,160,196Zm92-68a91.32,91.32,0,0,1-17.53,54,12,12,0,1,1-19.41-14.11,68,68,0,0,0-89.57-98.53,12,12,0,0,1-12.2-20.66A92,92,0,0,1,252,128Z"}))],["duotone",m.createElement(m.Fragment,null,m.createElement("path",{d:"M240,128a80,80,0,0,1-80,80H72A56,56,0,1,1,85.92,97.74l0,.1A80,80,0,0,1,240,128Z",opacity:"0.2"}),m.createElement("path",{d:"M53.92,34.62A8,8,0,1,0,42.08,45.38L81.32,88.55l-.06.12A65,65,0,0,0,72,88a64,64,0,0,0,0,128h88a87.34,87.34,0,0,0,31.8-5.93l10.28,11.31a8,8,0,1,0,11.84-10.76ZM160,200H72a48,48,0,0,1,0-96c1.1,0,2.2,0,3.3.12A88.4,88.4,0,0,0,72,128a8,8,0,0,0,16,0,72.25,72.25,0,0,1,5.06-26.54l87,95.7A71.66,71.66,0,0,1,160,200Zm88-72a87.89,87.89,0,0,1-22.35,58.61A8,8,0,0,1,213.71,176,72,72,0,0,0,117.37,70a8,8,0,0,1-9.48-12.89A88,88,0,0,1,248,128Z"}))],["fill",m.createElement(m.Fragment,null,m.createElement("path",{d:"M248,128.72A87.74,87.74,0,0,1,222.41,190a4,4,0,0,1-5.77-.16L103.78,65.67a4,4,0,0,1,.39-5.76A87.82,87.82,0,0,1,160.87,40C209.15,40.47,248.38,80.43,248,128.72ZM53.92,34.62A8,8,0,1,0,42.08,45.38L81.33,88.56l-.06.11A64,64,0,0,0,8,153c.53,35.12,29.84,63,65,63h87a87.65,87.65,0,0,0,31.78-5.95l10.3,11.33a8,8,0,0,0,11.33.52,8.32,8.32,0,0,0,.29-11.52Z"}))],["light",m.createElement(m.Fragment,null,m.createElement("path",{d:"M52.44,36A6,6,0,0,0,43.56,44l40.18,44.2c-.45.87-.9,1.75-1.32,2.64A62,62,0,1,0,72,214h88a85.23,85.23,0,0,0,32.35-6.3L203.56,220a6,6,0,0,0,8.88-8.08ZM160,202H72a50,50,0,1,1,5.9-99.64A86.25,86.25,0,0,0,74,128a6,6,0,0,0,12,0,73.92,73.92,0,0,1,6.44-30.2l91.22,100.34A73.65,73.65,0,0,1,160,202Zm86-74a85.85,85.85,0,0,1-21.85,57.27,6,6,0,0,1-4.47,2,6,6,0,0,1-4.47-10,74,74,0,0,0-99-108.92,6,6,0,1,1-7.11-9.67A86,86,0,0,1,246,128Z"}))],["regular",m.createElement(m.Fragment,null,m.createElement("path",{d:"M53.92,34.62A8,8,0,1,0,42.08,45.38L81.32,88.55l-.06.12A65,65,0,0,0,72,88a64,64,0,0,0,0,128h88a87.34,87.34,0,0,0,31.8-5.93l10.28,11.31a8,8,0,1,0,11.84-10.76ZM160,200H72a48,48,0,0,1,0-96c1.1,0,2.2,0,3.3.12A88.4,88.4,0,0,0,72,128a8,8,0,0,0,16,0,72.25,72.25,0,0,1,5.06-26.54l87,95.7A71.66,71.66,0,0,1,160,200Zm88-72a87.89,87.89,0,0,1-22.35,58.61A8,8,0,0,1,213.71,176,72,72,0,0,0,117.37,70a8,8,0,0,1-9.48-12.89A88,88,0,0,1,248,128Z"}))],["thin",m.createElement(m.Fragment,null,m.createElement("path",{d:"M51,37.31A4,4,0,0,0,45,42.69L86.16,87.93q-1.38,2.55-2.59,5.19A60,60,0,1,0,72,212h88a83.19,83.19,0,0,0,32.88-6.69L205,218.69a4,4,0,1,0,5.92-5.38ZM160,204H72a52,52,0,0,1,0-104,52.92,52.92,0,0,1,8.54.72A84.21,84.21,0,0,0,76,128a4,4,0,0,0,8,0,76,76,0,0,1,7.9-33.76L187.13,199A75.37,75.37,0,0,1,160,204Zm84-76a83.86,83.86,0,0,1-21.34,55.94,4,4,0,1,1-6-5.33A76,76,0,0,0,115,66.75a4,4,0,0,1-4.74-6.45A84,84,0,0,1,244,128Z"}))]]);var p=e(u(),1),ko=new Map([["bold",p.createElement(p.Fragment,null,p.createElement("path",{d:"M160,36A92.09,92.09,0,0,0,79,84.36,68,68,0,1,0,72,220h88a92,92,0,0,0,0-184Zm0,160H72a44,44,0,0,1-1.82-88A91.86,91.86,0,0,0,68,128a12,12,0,0,0,24,0,68,68,0,1,1,68,68Zm32.49-79.51L177,132l15.52,15.51a12,12,0,0,1-17,17L160,149l-15.51,15.52a12,12,0,1,1-17-17L143,132l-15.52-15.51a12,12,0,1,1,17-17L160,115l15.51-15.52a12,12,0,0,1,17,17Z"}))],["duotone",p.createElement(p.Fragment,null,p.createElement("path",{d:"M240,127.62a80,80,0,0,1-80,80H72A56,56,0,1,1,85.92,97.36l0,.1A80,80,0,0,1,240,127.62Z",opacity:"0.2"}),p.createElement("path",{d:"M160,40A88.09,88.09,0,0,0,81.29,88.67,64,64,0,1,0,72,216h88a88,88,0,0,0,0-176Zm0,160H72a48,48,0,0,1,0-96c1.1,0,2.2,0,3.29.11A88,88,0,0,0,72,128a8,8,0,0,0,16,0,72,72,0,1,1,72,72Zm29.66-82.34L171.31,136l18.35,18.34a8,8,0,0,1-11.32,11.32L160,147.31l-18.34,18.35a8,8,0,0,1-11.32-11.32L148.69,136l-18.35-18.34a8,8,0,0,1,11.32-11.32L160,124.69l18.34-18.35a8,8,0,0,1,11.32,11.32Z"}))],["fill",p.createElement(p.Fragment,null,p.createElement("path",{d:"M247.93,124.52C246.11,77.54,207.07,40,160.06,40A88.1,88.1,0,0,0,81.29,88.67h0A87.48,87.48,0,0,0,72,127.73,8.18,8.18,0,0,1,64.57,136,8,8,0,0,1,56,128a103.66,103.66,0,0,1,5.34-32.92,4,4,0,0,0-4.75-5.18A64.09,64.09,0,0,0,8,152c0,35.19,29.75,64,65,64H160A88.09,88.09,0,0,0,247.93,124.52Zm-58.27,29.82a8,8,0,0,1-11.32,11.32L160,147.31l-18.34,18.35a8,8,0,0,1-11.32-11.32L148.69,136l-18.35-18.34a8,8,0,0,1,11.32-11.32L160,124.69l18.34-18.35a8,8,0,0,1,11.32,11.32L171.31,136Z"}))],["light",p.createElement(p.Fragment,null,p.createElement("path",{d:"M160,42A86.11,86.11,0,0,0,82.43,90.88,62,62,0,1,0,72,214h88a86,86,0,0,0,0-172Zm0,160H72a50,50,0,0,1,0-100,50.67,50.67,0,0,1,5.91.35A85.61,85.61,0,0,0,74,128a6,6,0,0,0,12,0,74,74,0,1,1,74,74Zm28.24-85.76L168.48,136l19.76,19.76a6,6,0,1,1-8.48,8.48L160,144.48l-19.76,19.76a6,6,0,0,1-8.48-8.48L151.52,136l-19.76-19.76a6,6,0,0,1,8.48-8.48L160,127.52l19.76-19.76a6,6,0,0,1,8.48,8.48Z"}))],["regular",p.createElement(p.Fragment,null,p.createElement("path",{d:"M160,40A88.09,88.09,0,0,0,81.29,88.67,64,64,0,1,0,72,216h88a88,88,0,0,0,0-176Zm0,160H72a48,48,0,0,1,0-96c1.1,0,2.2,0,3.29.11A88,88,0,0,0,72,128a8,8,0,0,0,16,0,72,72,0,1,1,72,72Zm29.66-82.34L171.31,136l18.35,18.34a8,8,0,0,1-11.32,11.32L160,147.31l-18.34,18.35a8,8,0,0,1-11.32-11.32L148.69,136l-18.35-18.34a8,8,0,0,1,11.32-11.32L160,124.69l18.34-18.35a8,8,0,0,1,11.32,11.32Z"}))],["thin",p.createElement(p.Fragment,null,p.createElement("path",{d:"M160,44A84.11,84.11,0,0,0,83.59,93.12,60.71,60.71,0,0,0,72,92a60,60,0,0,0,0,120h88a84,84,0,0,0,0-168Zm0,160H72a52,52,0,1,1,8.55-103.3A83.66,83.66,0,0,0,76,128a4,4,0,0,0,8,0,76,76,0,1,1,76,76Zm26.83-89.17L165.66,136l21.17,21.17a4,4,0,0,1-5.66,5.66L160,141.66l-21.17,21.17a4,4,0,0,1-5.66-5.66L154.34,136l-21.17-21.17a4,4,0,0,1,5.66-5.66L160,130.34l21.17-21.17a4,4,0,1,1,5.66,5.66Z"}))]]);var y=e(u(),1);var To=e(u(),1),yo=(0,To.createContext)({color:"currentColor",size:"1em",weight:"regular",mirrored:!1});var P=y.forwardRef((o,a)=>{let{alt:s,color:r,size:n,weight:I,mirrored:h,children:B,weights:F,...D}=o,{color:H="currentColor",size:C,weight:Y="regular",mirrored:M=!1,...x}=y.useContext(yo);return y.createElement("svg",{ref:a,xmlns:"http://www.w3.org/2000/svg",width:n??C,height:n??C,fill:r??H,viewBox:"0 0 256 256",transform:h||M?"scale(-1, 1)":void 0,...x,...D},!!s&&y.createElement("title",null,s),B,F.get(I??Y))});P.displayName="IconBase";var W=e(u(),1);var q=W.forwardRef((o,a)=>W.createElement(P,{ref:a,...o,weights:bo}));q.displayName="CircleNotchIcon";var V=e(u(),1);var z=V.forwardRef((o,a)=>V.createElement(P,{ref:a,...o,weights:Po}));z.displayName="CloudCheckIcon";var X=e(u(),1);var _=X.forwardRef((o,a)=>X.createElement(P,{ref:a,...o,weights:Ao}));_.displayName="CloudSlashIcon";var Z=e(u(),1);var J=Z.forwardRef((o,a)=>Z.createElement(P,{ref:a,...o,weights:ko}));J.displayName="CloudXIcon";var Bo=e(S()),sa=o=>(0,Bo.jsx)(q,{size:o.size,className:"plotly-cloud-spin plotly-cloud-gray-icon",weight:"bold"}),K=sa;var R=e(S()),ra=({status:o,size:a=16,isButtonIcon:s})=>o==="BUILDING"||o==="STARTING"||o==="unknown"?(0,R.jsx)(K,{size:a}):o==="RUNNING"?(0,R.jsx)(z,{size:a,className:"plotly-cloud-green-icon",weight:s?"fill":"duotone"}):o==="new"?(0,R.jsx)(_,{size:a,className:"plotly-cloud-gray-icon",weight:s?"fill":"duotone"}):(0,R.jsx)(J,{size:a,className:"plotly-cloud-red-icon",weight:"duotone"}),j=ra;var d=e(S()),ea=({status:o,onPublish:a,isUpdate:s,publishing:r,onCancel:n})=>{let I=()=>{n&&n()},[h,B,F]=(0,Q.useMemo)(()=>o==="BUILDING"||o==="STARTING"?["App is building","Wait for your app to finish publishing before publishing again","gray"]:o==="RUNNING"?["App is live","Your app is running in Plotly Cloud.","green"]:["App is stopped","Start your app by publishing again or starting it in the app manager","red"],[o]),[D,H]=(0,Q.useMemo)(()=>r?s?["Open Settings in Plotly Cloud","Republishing"]:["Cancel","Publishing"]:s?["Open Settings in Plotly Cloud","Republish"]:["Cancel","Publish"],[r,s]),C=(0,Q.useMemo)(()=>["BUILDING","STARTING"].includes(o),[o]);return(0,d.jsxs)("div",{className:"plotly-cloud-status-banner",children:[(0,d.jsxs)("div",{className:`plotly-cloud-status-info plotly-cloud-status-${F}`,children:[(0,d.jsx)(j,{status:o,size:20}),(0,d.jsxs)("div",{className:"plotly-cloud-status-description",children:[(0,d.jsx)("div",{className:"plotly-cloud-status-title",children:h}),(0,d.jsx)("div",{className:"plotly-cloud-status-text",children:B})]})]}),(0,d.jsxs)("div",{className:"plotly-cloud-button-bar",children:[(0,d.jsx)("button",{type:"button",onClick:I,disabled:s?!1:r,className:"plotly-cloud-publish-modal-button plotly-cloud-btn-secondary",children:D}),(0,d.jsxs)("button",{type:"button",onClick:a,className:"plotly-cloud-publish-modal-button plotly-cloud-btn-primary",disabled:r||C,children:[r?(0,d.jsx)(K,{size:12}):null,H]})]})]})},so=ea;var g=e(S()),na=()=>{let[o,a]=(0,t.useState)(!1),[s,r]=(0,t.useState)(""),[n,I]=(0,t.useState)("initialize"),[h,B]=(0,t.useState)(!1),[F,D]=(0,t.useState)(!1),[H,C]=(0,t.useState)(!1),[Y,M]=(0,t.useState)(!1),[x,Fo]=(0,t.useState)(""),[Do,eo]=(0,t.useState)(""),[$,T]=(0,t.useState)("unknown"),[ta,Ho]=(0,t.useState)(!1),[N,U]=(0,t.useState)(-1),[Mo,Ro]=(0,t.useState)(1),v=(0,t.useRef)(""),A=(0,t.useRef)(""),[ca,la]=(0,t.useReducer)((i,f)=>{switch(f.type){case"init":return{...f.payload};case"slow_down":return{...i,delay:f.payload.delay};case"try_again":case"success":return{}}return{...i}},{}),w=(0,t.useMemo)(()=>Co("/"),[]),ma=()=>{a(!0)},oo=()=>{a(!1)},xo=()=>{a(!o)},No=(0,t.useMemo)(()=>o?"plotly-cloud-publish-container dash-debug-menu__button dash-debug-menu__button--selected":"plotly-cloud-publish-container dash-debug-menu__button",[o]),no=()=>{D(!0),w.authenticate().then(i=>{i.error?r(i.error):(I("auth_poll"),v.current=i.result.device_code,window.open(i.result.verification_uri_complete,"_blank"),U(setTimeout(()=>{w.auth_poll({device_code:v.current}).then(io)},8e3)))})},io=i=>{if(i.error)r(i.error);else{if(i.result.success){B(!0),v.current="",A.current?(T("unknown"),I("update_app"),w.status({app_id:A.current}).then(E=>{T(E.result.status)})):I("enter_name");return}if(i.result.try_again){no();return}let f=Mo||1;i.result.delay&&(f=i.result.delay,Ro(f)),U(setTimeout(()=>{w.auth_poll({device_code:v.current}).then(io)},1e3+f*1e3))}},Uo=no;(0,t.useEffect)(()=>{n==="initialize"&&w.initialize().then(i=>{let{result:f,error:E}=i;E?r(E):(B(!f?.authenticated),Fo(f.app_path),eo(f.app_name),T(f.status),f.app_id&&(A.current=f.app_id),f?.authenticated?f.status==="new"?I("enter_name"):I("update_app"):I("sign_in"))})},[n,o]);let to=i=>{T(i.result.status),i.result.status==="RUNNING"?(C(!1),M(!0),I("update_app")):["BUILDING","STARTING"].includes(i.result.status)?U(setTimeout(()=>{w.status({app_id:A.current}).then(to)},2e3)):(C(!1),I("update_app"))},co=i=>{if(i.error){r(i.error);return}I("status_poll"),A.current=i.result.app_id,T("BUILDING"),N>0&&clearTimeout(N),U(setTimeout(()=>{w.status({app_id:A.current}).then(to)},2500))},vo=i=>{Ho(!0),eo(i),C(!0),M(!1),T("BUILDING"),w.publish({app_path:x,app_name:i}).then(co)},lo=()=>{C(!0),M(!0),T("BUILDING"),w.publish({app_id:A.current,app_path:x}).then(co)};(0,t.useEffect)(()=>()=>{N&&clearTimeout(N)});let Eo=()=>{window.open(`https://cloud.plotly.com/app/${A.current}/settings`,"_blank")};return(0,g.jsxs)("div",{className:No,children:[(0,g.jsxs)("button",{className:"plotly-cloud-publish-button",onClick:xo,children:[(0,g.jsx)(j,{status:$,isButtonIcon:!0})," Plotly Cloud"]},"publish-button"),o?(0,g.jsx)(So,{title:"Plotly Cloud",onClose:oo,children:s||(()=>{switch(n){case"authenticate":case"auth_poll":case"sign_in":return(0,g.jsx)(ho,{onSignIn:Uo,waiting:F});case"enter_name":return(0,g.jsx)(Lo,{onPublish:vo,onCancel:oo,initialAppName:Do});case"update_app":return(0,g.jsx)(so,{onPublish:lo,status:$,isUpdate:!0,onCancel:Eo});case"status_poll":case"publish":return(0,g.jsx)(so,{publishing:H,onPublish:lo,status:$,onCancel:oo,isUpdate:Y});default:return(0,g.jsx)(g.Fragment,{})}})()},"publish-modal"):null]})},ro=na;typeof window<"u"&&(window.plotly_cloud_publish_component={PlotlyCloudPublishComponent:ro});return Xo(ia);})();
3
+ /*! Bundled license information:
4
+
5
+ react/cjs/react-jsx-runtime.production.min.js:
6
+ (**
7
+ * @license React
8
+ * react-jsx-runtime.production.min.js
9
+ *
10
+ * Copyright (c) Facebook, Inc. and its affiliates.
11
+ *
12
+ * This source code is licensed under the MIT license found in the
13
+ * LICENSE file in the root directory of this source tree.
14
+ *)
15
+ */
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "plotly-cloud"
7
- version = "0.1.0rc1"
7
+ version = "0.1.0rc2"
8
8
  description = "CLI for interacting with Plotly Cloud to deploy Dash apps"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -21,6 +21,8 @@ dependencies = [
21
21
  [project.scripts]
22
22
  plotly = "plotly_cloud.cli:main"
23
23
 
24
+ [project.entry-points.dash_hooks]
25
+ plotly_cloud_devtools = "plotly_cloud._devtool_hooks"
24
26
 
25
27
  [tool.ruff]
26
28
  line-length = 120
@@ -35,6 +37,8 @@ ignore-vcs = true
35
37
  include = [
36
38
  "plotly_cloud/**/*.py",
37
39
  "plotly_cloud/cloud-env.toml",
40
+ "plotly_cloud/cloud_devtools.js",
41
+ "plotly_cloud/cloud_devtools.css",
38
42
  ]
39
43
 
40
44
  [tool.hatch.build.targets.wheel]
File without changes