ntc-dev-kit 1.3.1__tar.gz → 1.4.0__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.
- {ntc_dev_kit-1.3.1/ntc_dev_kit.egg-info → ntc_dev_kit-1.4.0}/PKG-INFO +32 -2
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/README.md +31 -1
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/__init__.py +1 -1
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/cli_enhanced.py +7 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/next.config.js +5 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/vite.config.ts +2 -2
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/App.css +12 -0
- ntc_dev_kit-1.4.0/ntc/templates/react_structure/src/App.tsx +79 -0
- ntc_dev_kit-1.4.0/ntc/templates/react_structure/src/utils/constants.ts +1 -0
- ntc_dev_kit-1.4.0/ntc/templates/react_structure/vite.config.ts +13 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/wizard.py +6 -0
- ntc_dev_kit-1.4.0/ntc/workspace/adopt.py +316 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/config.py +51 -9
- ntc_dev_kit-1.4.0/ntc/workspace/console_window.py +48 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/create.py +3 -4
- ntc_dev_kit-1.4.0/ntc/workspace/origins.py +182 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/processes.py +4 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/screen.py +17 -2
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/setup.py +24 -11
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/start.py +7 -1
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0/ntc_dev_kit.egg-info}/PKG-INFO +32 -2
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc_dev_kit.egg-info/SOURCES.txt +4 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/pyproject.toml +1 -1
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/setup.py +1 -1
- ntc_dev_kit-1.4.0/tests/test_adopt.py +108 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/tests/test_workspace.py +36 -5
- ntc_dev_kit-1.3.1/ntc/templates/react_structure/src/App.tsx +0 -63
- ntc_dev_kit-1.3.1/ntc/templates/react_structure/src/utils/constants.ts +0 -1
- ntc_dev_kit-1.3.1/ntc/templates/react_structure/vite.config.ts +0 -12
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/LICENSE +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/MANIFEST.in +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/app_creator.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/cli.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/composer/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/composer/main.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/deployment/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/main.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/admin.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/apps.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/migrations/0001_initial.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/models.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/serializers.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/tasks.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/templates/authentication/password_reset.html +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/templates/authentication/password_reset.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/tests.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/urls.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/views.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/drf_structure.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/main.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/structure.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/.entry/start_core_backend.sh +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/.entry/start_tasks_backend.sh +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/CLAUDE.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/README.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/app_admin.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/app_apps.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/app_models.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/app_urls.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/app_views.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/common_apps.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/common_checks.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/common_models.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/ci/github_ci.yml +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/ci/pre_commit.yaml +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/config/gunicorn_config.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/config/http.conf +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/exceptions/handler.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/formatter.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/health.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/logger.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/pagination.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/renderers.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/response_wrapper.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/schema.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/services/azure/email/backend.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/services/azure/email/client.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/services/azure/email/notifier.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/services/object_storage.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/tests.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/utils/request.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/asgi.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/celery.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/dev_settings.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/django_settings.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/prod_settings.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/project_init.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/schema.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/test_settings.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/urls.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/wsgi.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/docker/Dockerfile +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/docker/captain-definition +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/docker/docker-compose.yml +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/knowledge/knowledge_description.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/knowledge/knowledge_design_pattern.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/knowledge/knowledge_structure.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/README.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/django_env.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/django_requirements.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/django_requirements_dev.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/dockerignore.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/gitattributes.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/gitignore.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/manage.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/pytest_ini.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/ruff_toml.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/.gitignore +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/.knowledge/knowledge_description.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/.knowledge/knowledge_design_pattern.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/.knowledge/knowledge_structure.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/CLAUDE.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/analysis_options.yaml +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/core/config/app_config.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/core/config/environment.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/core/constants/app_constants.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/core/network/dio_client.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/features/home/presentation/home_screen.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/l10n/app_localizations.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/main.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/services/api_service.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/shared/localization/locale_provider.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/shared/theme/app_theme.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/shared/theme/theme_provider.dart +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/main.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/pubspec.yaml +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/structure.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/.env.example +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/.gitignore +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/CLAUDE.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/README.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/eslint.config.mjs +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/knowledge/knowledge_description.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/knowledge/knowledge_design_pattern.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/knowledge/knowledge_structure.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/main.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/package.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/public/logo.svg +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/about/page.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/contact/page.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/error.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/globals.css +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/icon.svg +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/layout.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/loading.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/manifest.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/not-found.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/opengraph-image.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/page.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/robots.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/sitemap.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/components/Footer.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/components/Header.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/services/api.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/services/server.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/utils/constants.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/structure.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/tsconfig.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/.env.example +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/.gitignore +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/CLAUDE.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/README.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/eslint.config.js +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/index.html +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/knowledge/knowledge_description.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/knowledge/knowledge_design_pattern.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/knowledge/knowledge_structure.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/main.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/package.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/public/favicon.svg +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/App.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/components/ProtectedRoute.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/components/Sidebar.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/components/TopNav.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/context/AuthContext.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/context/ThemeContext.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/context/WorkspaceContext.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/hooks/useApi.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/index.css +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/layouts/AuthLayout.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/layouts/DashboardLayout.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/main.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/pages/DashboardPage.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/pages/auth/LoginPage.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/services/api.service.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/services/base.service.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/services/token.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/vite-env.d.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/structure.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/tsconfig.app.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/tsconfig.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/tsconfig.node.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/CLAUDE.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/index.css +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/index.html +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/knowledge/knowledge_description.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/knowledge/knowledge_design_pattern.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/knowledge/knowledge_structure.md +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/main.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/package.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/public/favicon.svg +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/src/components/Header.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/src/main.tsx +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/src/services/api.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/src/vite-env.d.ts +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/structure.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/tsconfig.app.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/tsconfig.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/tsconfig.node.json +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/ui/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/ui/app.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/environment.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/output.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/prompts.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/shell.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/validation.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/__init__.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/clean.py +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc_dev_kit.egg-info/dependency_links.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc_dev_kit.egg-info/entry_points.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc_dev_kit.egg-info/requires.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc_dev_kit.egg-info/top_level.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/requirements.txt +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/setup.cfg +0 -0
- {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/tests/test_templates.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ntc-dev-kit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Nexent Toolkit Console - A CLI tool for scaffolding Django REST Framework, React, Next.js, and Flutter projects
|
|
5
5
|
Home-page: https://github.com/nexent/ntc
|
|
6
6
|
Author: Nexent
|
|
@@ -163,11 +163,41 @@ In `ntc start`, **All** is a grid of up to four terminals, one per app (**[** an
|
|
|
163
163
|
through more), with each app's port and status on its border and its output colour-coded: errors
|
|
164
164
|
red, warnings amber, ready/compiled green, HTTP status codes by class. **Tab** moves between the
|
|
165
165
|
terminals and the keys act on the focused one: **r** restarts, **s** stops or starts, **o** opens
|
|
166
|
-
it in the browser, **i** types into it (Flutter's `r` hot reload), **c** clears, **Enter** opens it
|
|
166
|
+
it in the browser (**Ctrl+O** opens every app that's running), **i** types into it (Flutter's `r` hot reload), **c** clears, **Enter** opens it
|
|
167
167
|
full screen (**Esc** comes back). **1-9** jump to an app's own tab, **0** back to all, **q** quits
|
|
168
168
|
and stops everything. Apps wait for the ones they `need` (the backend) before starting, and a port
|
|
169
169
|
that's already taken is reported instead of crashing.
|
|
170
170
|
|
|
171
|
+
#### The backend allows the frontends
|
|
172
|
+
|
|
173
|
+
Whenever `ntc start` runs a Django app, it adds every frontend's origin (`http://127.0.0.1:<port>`
|
|
174
|
+
and `http://localhost:<port>`) to the backend's `CORS_ALLOWED_ORIGINS` and `CSRF_TRUSTED_ORIGINS`,
|
|
175
|
+
so the browser apps can call the API whatever ports they're on. It reads which environment
|
|
176
|
+
variables the settings use and keeps what they already allow (from the environment, the app's
|
|
177
|
+
`.env`, or the settings' default); only the backend's process gets the result, nothing is edited.
|
|
178
|
+
A setting written out in the settings (not read from the environment) can't be extended from
|
|
179
|
+
outside: the backend's terminal then says which origins to add. `allow_origins = false` in the
|
|
180
|
+
app's section turns this off. New workspaces also get the origins in the backend's `.env`, for
|
|
181
|
+
running it on its own.
|
|
182
|
+
|
|
183
|
+
#### Existing projects: `ntc adopt`
|
|
184
|
+
|
|
185
|
+
Apps that weren't made by ntc can run with it too. In the folder that holds them:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
ntc adopt # finds the apps, shows what it found, writes ntc.toml
|
|
189
|
+
ntc adopt --dry-run # just show
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
It recognises Django (`manage.py`), Next.js, Vite and other Node apps (`package.json` with a
|
|
193
|
+
`dev`/`start` script) and Flutter, and keeps how each already runs: its package manager (npm,
|
|
194
|
+
pnpm, yarn, bun), its `dev` script and port (a port clash is moved with `--port`), and its Python
|
|
195
|
+
environment (`env/`, `venv/`, `.venv/`, or a conda env named after the project). Folders that
|
|
196
|
+
aren't apps (documents, tooling) are left out, and a shared name prefix is dropped
|
|
197
|
+
(`shop-backend` → `backend`). Only `ntc.toml` is written: nothing in the apps changes, no env
|
|
198
|
+
values are overridden, and setup never runs `migrate` (the project's `.env` may point at a shared
|
|
199
|
+
database). `ntc setup` copies `.env.example` when an app has no env file yet.
|
|
200
|
+
|
|
171
201
|
`ntc clean` only removes what the tools rebuild (`.next`, `dist`, `__pycache__`, caches; with
|
|
172
202
|
`--deep` also `node_modules` and `env`), never anything git tracks, and skips apps that are running.
|
|
173
203
|
`--dry-run` shows what would go.
|
|
@@ -122,11 +122,41 @@ In `ntc start`, **All** is a grid of up to four terminals, one per app (**[** an
|
|
|
122
122
|
through more), with each app's port and status on its border and its output colour-coded: errors
|
|
123
123
|
red, warnings amber, ready/compiled green, HTTP status codes by class. **Tab** moves between the
|
|
124
124
|
terminals and the keys act on the focused one: **r** restarts, **s** stops or starts, **o** opens
|
|
125
|
-
it in the browser, **i** types into it (Flutter's `r` hot reload), **c** clears, **Enter** opens it
|
|
125
|
+
it in the browser (**Ctrl+O** opens every app that's running), **i** types into it (Flutter's `r` hot reload), **c** clears, **Enter** opens it
|
|
126
126
|
full screen (**Esc** comes back). **1-9** jump to an app's own tab, **0** back to all, **q** quits
|
|
127
127
|
and stops everything. Apps wait for the ones they `need` (the backend) before starting, and a port
|
|
128
128
|
that's already taken is reported instead of crashing.
|
|
129
129
|
|
|
130
|
+
#### The backend allows the frontends
|
|
131
|
+
|
|
132
|
+
Whenever `ntc start` runs a Django app, it adds every frontend's origin (`http://127.0.0.1:<port>`
|
|
133
|
+
and `http://localhost:<port>`) to the backend's `CORS_ALLOWED_ORIGINS` and `CSRF_TRUSTED_ORIGINS`,
|
|
134
|
+
so the browser apps can call the API whatever ports they're on. It reads which environment
|
|
135
|
+
variables the settings use and keeps what they already allow (from the environment, the app's
|
|
136
|
+
`.env`, or the settings' default); only the backend's process gets the result, nothing is edited.
|
|
137
|
+
A setting written out in the settings (not read from the environment) can't be extended from
|
|
138
|
+
outside: the backend's terminal then says which origins to add. `allow_origins = false` in the
|
|
139
|
+
app's section turns this off. New workspaces also get the origins in the backend's `.env`, for
|
|
140
|
+
running it on its own.
|
|
141
|
+
|
|
142
|
+
#### Existing projects: `ntc adopt`
|
|
143
|
+
|
|
144
|
+
Apps that weren't made by ntc can run with it too. In the folder that holds them:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
ntc adopt # finds the apps, shows what it found, writes ntc.toml
|
|
148
|
+
ntc adopt --dry-run # just show
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
It recognises Django (`manage.py`), Next.js, Vite and other Node apps (`package.json` with a
|
|
152
|
+
`dev`/`start` script) and Flutter, and keeps how each already runs: its package manager (npm,
|
|
153
|
+
pnpm, yarn, bun), its `dev` script and port (a port clash is moved with `--port`), and its Python
|
|
154
|
+
environment (`env/`, `venv/`, `.venv/`, or a conda env named after the project). Folders that
|
|
155
|
+
aren't apps (documents, tooling) are left out, and a shared name prefix is dropped
|
|
156
|
+
(`shop-backend` → `backend`). Only `ntc.toml` is written: nothing in the apps changes, no env
|
|
157
|
+
values are overridden, and setup never runs `migrate` (the project's `.env` may point at a shared
|
|
158
|
+
database). `ntc setup` copies `.env.example` when an app has no env file yet.
|
|
159
|
+
|
|
130
160
|
`ntc clean` only removes what the tools rebuild (`.next`, `dist`, `__pycache__`, caches; with
|
|
131
161
|
`--deep` also `node_modules` and `env`), never anything git tracks, and skips apps that are running.
|
|
132
162
|
`--dry-run` shows what would go.
|
|
@@ -363,6 +363,10 @@ Examples:
|
|
|
363
363
|
add_install.add_argument("--install", dest="install", action="store_true", default=None)
|
|
364
364
|
add_install.add_argument("--no-install", dest="install", action="store_false")
|
|
365
365
|
add_parser.add_argument("--yes", "-y", action="store_true", help="Don't ask")
|
|
366
|
+
adopt_parser = subparsers.add_parser("adopt", help="Run existing apps (not made by ntc) with ntc: writes ntc.toml")
|
|
367
|
+
adopt_parser.add_argument("path", nargs="?", help="The folder holding the apps (default: current folder)")
|
|
368
|
+
adopt_parser.add_argument("--dry-run", action="store_true", help="Show what was found, write nothing")
|
|
369
|
+
adopt_parser.add_argument("--yes", "-y", action="store_true", help="Don't ask: take the best guesses")
|
|
366
370
|
setup_parser = subparsers.add_parser("setup", help="Install the workspace's apps (after a clone or a deep clean)")
|
|
367
371
|
setup_parser.add_argument("apps", nargs="*", help="Only these apps")
|
|
368
372
|
clean_parser = subparsers.add_parser("clean", help="Delete build output and caches; say how much space it freed")
|
|
@@ -424,6 +428,9 @@ Examples:
|
|
|
424
428
|
elif args.command == "start":
|
|
425
429
|
from ntc.workspace.start import run as run_start
|
|
426
430
|
sys.exit(0 if run_start(args.apps, plain=args.plain) else 1)
|
|
431
|
+
elif args.command == "adopt":
|
|
432
|
+
from ntc.workspace.adopt import run as run_adopt
|
|
433
|
+
sys.exit(0 if run_adopt(args.path, assume_yes=args.yes, dry_run=args.dry_run) else 1)
|
|
427
434
|
elif args.command == "setup":
|
|
428
435
|
from ntc.workspace.setup import run as run_setup
|
|
429
436
|
sys.exit(0 if run_setup(args.apps) else 1)
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/** @type {import('next').NextConfig} */
|
|
2
2
|
const nextConfig = {
|
|
3
3
|
reactStrictMode: true,
|
|
4
|
+
// The dev server serves on localhost; opened as 127.0.0.1 (as ntc start may), its hot reload
|
|
5
|
+
// would be blocked as cross-origin.
|
|
6
|
+
allowedDevOrigins: ['127.0.0.1', 'localhost'],
|
|
7
|
+
// The project folder is the root, even when a package-lock.json sits in a folder above it.
|
|
8
|
+
turbopack: { root: __dirname },
|
|
4
9
|
images: {
|
|
5
10
|
// Hosts next/image may load from, e.g.
|
|
6
11
|
// { protocol: 'https', hostname: 'media.example.com', pathname: '/**' }
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { defineConfig } from 'vite'
|
|
2
2
|
import react from '@vitejs/plugin-react'
|
|
3
3
|
import tailwindcss from '@tailwindcss/vite'
|
|
4
|
-
import
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
5
|
|
|
6
6
|
export default defineConfig({
|
|
7
7
|
plugins: [react(), tailwindcss()],
|
|
8
8
|
resolve: {
|
|
9
9
|
alias: {
|
|
10
|
-
'@':
|
|
10
|
+
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
|
11
11
|
},
|
|
12
12
|
},
|
|
13
13
|
})
|
|
@@ -42,6 +42,18 @@
|
|
|
42
42
|
background: rgba(244, 67, 54, 0.3);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
.status-indicator.degraded {
|
|
46
|
+
background: rgba(255, 152, 0, 0.35);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.api-problems,
|
|
50
|
+
.api-hint {
|
|
51
|
+
margin-top: 0.75rem;
|
|
52
|
+
list-style: none;
|
|
53
|
+
font-size: 0.9rem;
|
|
54
|
+
opacity: 0.9;
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
.status-indicator.checking {
|
|
46
58
|
background: rgba(255, 193, 7, 0.3);
|
|
47
59
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
import './App.css'
|
|
3
|
+
import Header from './components/Header'
|
|
4
|
+
import axios from 'axios'
|
|
5
|
+
import { apiService } from './services/api'
|
|
6
|
+
import { API_BASE_URL } from './utils/constants'
|
|
7
|
+
|
|
8
|
+
type ApiStatus = 'checking' | 'connected' | 'degraded' | 'disconnected'
|
|
9
|
+
|
|
10
|
+
function App() {
|
|
11
|
+
const [apiStatus, setApiStatus] = useState<ApiStatus>('checking')
|
|
12
|
+
const [problems, setProblems] = useState<string[]>([])
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
// The ntc Django template's /health/ answers 503 when the API is up but a service it uses
|
|
16
|
+
// (the database, Redis) isn't: that is "connected, degraded", not "disconnected".
|
|
17
|
+
const checkApiConnection = async () => {
|
|
18
|
+
try {
|
|
19
|
+
await apiService.get('/health/')
|
|
20
|
+
setApiStatus('connected')
|
|
21
|
+
} catch (error) {
|
|
22
|
+
const response = axios.isAxiosError(error) ? error.response : undefined
|
|
23
|
+
if (!response) {
|
|
24
|
+
setApiStatus('disconnected') // nothing answered: not running, wrong URL, or CORS
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
const services = (response.data?.errors ?? {}) as Record<string, { ok?: boolean; problem?: string }>
|
|
28
|
+
setProblems(Object.entries(services)
|
|
29
|
+
.filter(([, service]) => service && typeof service === 'object' && service.ok === false)
|
|
30
|
+
.map(([name, service]) => `${name}: ${service.problem}`))
|
|
31
|
+
setApiStatus('degraded')
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
checkApiConnection()
|
|
36
|
+
}, [])
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="App">
|
|
40
|
+
<Header />
|
|
41
|
+
<main className="App-main">
|
|
42
|
+
<div className="welcome-section">
|
|
43
|
+
<h1>Welcome to {import.meta.env.VITE_APP_NAME || '{project_name}'}</h1>
|
|
44
|
+
<p>Your API-driven React application is ready!</p>
|
|
45
|
+
|
|
46
|
+
<div className="api-status">
|
|
47
|
+
<h3>API Connection Status:</h3>
|
|
48
|
+
<div className={`status-indicator ${apiStatus}`}>
|
|
49
|
+
{apiStatus === 'checking' && '🔄 Checking...'}
|
|
50
|
+
{apiStatus === 'connected' && '✅ Connected'}
|
|
51
|
+
{apiStatus === 'degraded' && '⚠️ Connected, but a service is down'}
|
|
52
|
+
{apiStatus === 'disconnected' && '❌ Disconnected'}
|
|
53
|
+
</div>
|
|
54
|
+
{problems.length > 0 && (
|
|
55
|
+
<ul className="api-problems">
|
|
56
|
+
{problems.map((problem) => <li key={problem}>{problem}</li>)}
|
|
57
|
+
</ul>
|
|
58
|
+
)}
|
|
59
|
+
{apiStatus === 'disconnected' && (
|
|
60
|
+
<p className="api-hint">Is the backend running? It should be at {API_BASE_URL}.</p>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div className="getting-started">
|
|
65
|
+
<h3>Getting Started:</h3>
|
|
66
|
+
<ol>
|
|
67
|
+
<li>Start your backend API server</li>
|
|
68
|
+
<li>Update API_BASE_URL in .env file if needed</li>
|
|
69
|
+
<li>Begin building your components in src/components/</li>
|
|
70
|
+
<li>Add API calls using the apiService in src/services/</li>
|
|
71
|
+
</ol>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</main>
|
|
75
|
+
</div>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default App
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:8000/api/v1' // the ntc Django template's API
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [react()],
|
|
7
|
+
resolve: {
|
|
8
|
+
alias: {
|
|
9
|
+
// Not __dirname: Vite's native config loader (the coming default) runs it as ESM.
|
|
10
|
+
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
})
|
|
@@ -255,6 +255,12 @@ def run(technology=None, name=None, path=None, with_auth=None, env_choice=None,
|
|
|
255
255
|
inside = workspace_config.find_root()
|
|
256
256
|
if inside and not apps and technology is None:
|
|
257
257
|
prompts.info(f"You're in the {os.path.basename(inside)} workspace - `ntc add` adds an app to it.")
|
|
258
|
+
elif not inside and not apps and technology is None:
|
|
259
|
+
from ntc.workspace.adopt import detect
|
|
260
|
+
existing, _ = detect(os.getcwd())
|
|
261
|
+
if existing:
|
|
262
|
+
prompts.info(f"This folder already has {len(existing)} app{'s' if len(existing) != 1 else ''} "
|
|
263
|
+
f"({', '.join(a.folder for a in existing)}) - `ntc adopt` sets them up for ntc start.")
|
|
258
264
|
plan = Plan()
|
|
259
265
|
try:
|
|
260
266
|
if name is None:
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""`ntc adopt`: run existing apps (not made by ntc) with ntc start / setup / clean.
|
|
2
|
+
|
|
3
|
+
It looks at the folder's subfolders (or the folder itself, when that is an app), works out what
|
|
4
|
+
each one is and how it already runs - its package manager, its dev script and port, its Python
|
|
5
|
+
environment - and writes ntc.toml. Nothing inside the apps is changed: no files written, no env
|
|
6
|
+
overrides, and no `migrate` in setup, since an existing project's .env may point at a shared or
|
|
7
|
+
production database.
|
|
8
|
+
"""
|
|
9
|
+
import glob
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
from colorama import Fore, Style
|
|
16
|
+
|
|
17
|
+
from ntc.utils import prompts
|
|
18
|
+
from ntc.workspace import config
|
|
19
|
+
|
|
20
|
+
DIM = Fore.LIGHTBLACK_EX
|
|
21
|
+
RESET = Style.RESET_ALL
|
|
22
|
+
|
|
23
|
+
DEFAULT_PORTS = {"django": 8000, "nextjs": 3000, "vite": 5173, "node": 3000}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Detected:
|
|
27
|
+
def __init__(self, folder, kind):
|
|
28
|
+
self.folder = folder # path relative to the workspace root ("." for the root)
|
|
29
|
+
self.kind = kind
|
|
30
|
+
self.name = folder
|
|
31
|
+
self.port = None
|
|
32
|
+
self.port_fixed = False # the dev script sets it: can't move it without editing the app
|
|
33
|
+
self.manager = "npm"
|
|
34
|
+
self.script = "dev"
|
|
35
|
+
self.python = None # (path, kind, description) for Django
|
|
36
|
+
self.python_options = []
|
|
37
|
+
self.notes = []
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ── Detection ───────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
def _read_json(path):
|
|
43
|
+
try:
|
|
44
|
+
with open(path, encoding="utf-8") as f:
|
|
45
|
+
return json.load(f)
|
|
46
|
+
except (OSError, ValueError):
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _detect_node(path, folder):
|
|
51
|
+
package = _read_json(os.path.join(path, "package.json"))
|
|
52
|
+
if package is None:
|
|
53
|
+
return None
|
|
54
|
+
deps = {**package.get("dependencies", {}), **package.get("devDependencies", {})}
|
|
55
|
+
scripts = package.get("scripts", {})
|
|
56
|
+
if "next" in deps:
|
|
57
|
+
kind = "nextjs"
|
|
58
|
+
elif "vite" in deps:
|
|
59
|
+
kind = "vite"
|
|
60
|
+
elif scripts.get("dev") or scripts.get("start"):
|
|
61
|
+
kind = "node"
|
|
62
|
+
else:
|
|
63
|
+
return None # a library or tooling package, not something to run
|
|
64
|
+
app = Detected(folder, kind)
|
|
65
|
+
for lock, manager in (("pnpm-lock.yaml", "pnpm"), ("yarn.lock", "yarn"), ("bun.lockb", "bun"), ("bun.lock", "bun")):
|
|
66
|
+
if os.path.exists(os.path.join(path, lock)):
|
|
67
|
+
app.manager = manager
|
|
68
|
+
break
|
|
69
|
+
app.script = "dev" if "dev" in scripts else "start"
|
|
70
|
+
script = scripts.get(app.script, "")
|
|
71
|
+
match = re.search(r"(?:--port|-p)[ =](\d{2,5})\b", script)
|
|
72
|
+
if match:
|
|
73
|
+
app.port, app.port_fixed = int(match.group(1)), True
|
|
74
|
+
elif kind == "vite":
|
|
75
|
+
for name in glob.glob(os.path.join(path, "vite.config.*")):
|
|
76
|
+
try:
|
|
77
|
+
text = open(name, encoding="utf-8").read()
|
|
78
|
+
except OSError:
|
|
79
|
+
continue
|
|
80
|
+
match = re.search(r"\bport\s*:\s*(\d{2,5})", text)
|
|
81
|
+
if match:
|
|
82
|
+
app.port, app.port_fixed = int(match.group(1)), True
|
|
83
|
+
if app.port is None:
|
|
84
|
+
app.port = DEFAULT_PORTS[kind]
|
|
85
|
+
if not os.path.isdir(os.path.join(path, "node_modules")):
|
|
86
|
+
app.notes.append("dependencies not installed")
|
|
87
|
+
return app
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _conda_envs():
|
|
91
|
+
"""{name: path} of the conda environments on this machine (without running conda)."""
|
|
92
|
+
import shutil
|
|
93
|
+
from ntc.templates.drf_structure.main import find_conda_executable
|
|
94
|
+
home = os.path.expanduser("~")
|
|
95
|
+
roots = {os.path.join(home, ".conda", "envs")}
|
|
96
|
+
for base in ("anaconda3", "miniconda3", "miniforge3", "mambaforge"):
|
|
97
|
+
roots.add(os.path.join(home, base, "envs"))
|
|
98
|
+
roots.add(os.path.join(os.environ.get("PROGRAMDATA", r"C:\ProgramData"), base, "envs"))
|
|
99
|
+
for conda in (os.environ.get("CONDA_EXE"), shutil.which(find_conda_executable() or "conda")):
|
|
100
|
+
if conda:
|
|
101
|
+
# ...\anaconda3\Scripts\conda.exe, ...\anaconda3\condabin\conda.bat or .../bin/conda
|
|
102
|
+
roots.add(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(conda))), "envs"))
|
|
103
|
+
envs = {}
|
|
104
|
+
for root in roots:
|
|
105
|
+
for path in glob.glob(os.path.join(root, "*")):
|
|
106
|
+
if os.path.isdir(os.path.join(path, "conda-meta")):
|
|
107
|
+
envs[os.path.basename(path)] = path
|
|
108
|
+
return envs
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _words(text):
|
|
112
|
+
return {w for w in re.split(r"[^a-z0-9]+", text.lower()) if len(w) > 2} - {"backend", "api", "server", "app", "env"}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _detect_python(path, folder, workspace_name, package):
|
|
116
|
+
"""The candidates for the app's Python, best first: [(path, kind, description)]."""
|
|
117
|
+
options = []
|
|
118
|
+
for name in ("env", "venv", ".venv"):
|
|
119
|
+
candidate = os.path.join(path, name)
|
|
120
|
+
if os.path.exists(os.path.join(candidate, "pyvenv.cfg")):
|
|
121
|
+
options.append((name, "venv", f"{name}/ in the app"))
|
|
122
|
+
elif os.path.isdir(os.path.join(candidate, "conda-meta")):
|
|
123
|
+
options.append((name, "conda", f"conda env {name}/ in the app"))
|
|
124
|
+
# A conda environment named after the project (longuo for longuo-backend / longuo_backend).
|
|
125
|
+
wanted = _words(folder) | _words(workspace_name) | _words(package or "")
|
|
126
|
+
exact = {folder.lower(), (package or "").lower(), workspace_name.lower()}
|
|
127
|
+
scored = []
|
|
128
|
+
for name, env_path in _conda_envs().items():
|
|
129
|
+
score = 2 if name.lower() in exact else len(_words(name) & wanted)
|
|
130
|
+
if score:
|
|
131
|
+
scored.append((score, name, env_path))
|
|
132
|
+
for _, name, env_path in sorted(scored, reverse=True):
|
|
133
|
+
options.append((env_path, "conda", f"conda env '{name}'"))
|
|
134
|
+
for variable, kind in (("VIRTUAL_ENV", "venv"), ("CONDA_PREFIX", "conda")):
|
|
135
|
+
active = os.environ.get(variable)
|
|
136
|
+
if active and not any(o[0] == active for o in options):
|
|
137
|
+
options.append((active, kind, f"the active environment ({os.path.basename(active)})"))
|
|
138
|
+
options.append(("env", "venv", "a new venv, env/ in the app (`ntc setup` creates it)"))
|
|
139
|
+
return options
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def detect_app(path, folder, workspace_name):
|
|
143
|
+
if os.path.exists(os.path.join(path, "manage.py")):
|
|
144
|
+
app = Detected(folder, "django")
|
|
145
|
+
app.port = DEFAULT_PORTS["django"]
|
|
146
|
+
from ntc.workspace.setup import django_package
|
|
147
|
+
package = django_package(path)
|
|
148
|
+
app.package = package
|
|
149
|
+
app.python_options = _detect_python(path, folder, workspace_name, package)
|
|
150
|
+
app.python = app.python_options[0]
|
|
151
|
+
app.requirements = next((r for r in ("requirements-dev.txt", "requirements.txt")
|
|
152
|
+
if os.path.exists(os.path.join(path, r))), None)
|
|
153
|
+
if app.python[2].startswith("a new venv"):
|
|
154
|
+
app.notes.append("no Python environment yet")
|
|
155
|
+
return app
|
|
156
|
+
if os.path.exists(os.path.join(path, "pubspec.yaml")):
|
|
157
|
+
app = Detected(folder, "flutter")
|
|
158
|
+
return app
|
|
159
|
+
return _detect_node(path, folder)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def detect(root):
|
|
163
|
+
"""The apps in root: its subfolders that are apps, or root itself when it is one."""
|
|
164
|
+
name = os.path.basename(os.path.abspath(root))
|
|
165
|
+
apps, skipped = [], []
|
|
166
|
+
for entry in sorted(os.listdir(root)):
|
|
167
|
+
path = os.path.join(root, entry)
|
|
168
|
+
if not os.path.isdir(path) or entry.startswith(".") or entry in ("node_modules", "env", "venv"):
|
|
169
|
+
continue
|
|
170
|
+
app = detect_app(path, entry, name)
|
|
171
|
+
(apps.append(app) if app else skipped.append(entry))
|
|
172
|
+
if not apps:
|
|
173
|
+
app = detect_app(root, ".", name)
|
|
174
|
+
if app:
|
|
175
|
+
app.name = name
|
|
176
|
+
apps.append(app)
|
|
177
|
+
skipped = []
|
|
178
|
+
_short_names(apps)
|
|
179
|
+
_spread_ports(apps)
|
|
180
|
+
return apps, skipped
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _short_names(apps):
|
|
184
|
+
"""longuo-backend, longuo-crm ... -> backend, crm: the shared prefix says nothing."""
|
|
185
|
+
names = [a.folder for a in apps if a.folder != "."]
|
|
186
|
+
if len(names) < 2:
|
|
187
|
+
return
|
|
188
|
+
prefix = os.path.commonprefix(names)
|
|
189
|
+
cut = max(prefix.rfind("-"), prefix.rfind("_"))
|
|
190
|
+
if cut <= 0:
|
|
191
|
+
return
|
|
192
|
+
short = [n[cut + 1:] for n in names]
|
|
193
|
+
if all(short) and len(set(short)) == len(short):
|
|
194
|
+
for app in apps:
|
|
195
|
+
if app.folder != ".":
|
|
196
|
+
app.name = app.folder[cut + 1:]
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _spread_ports(apps):
|
|
200
|
+
"""Two apps on one port can't both run: move the ones whose port isn't fixed by their script."""
|
|
201
|
+
taken = {a.port for a in apps if a.port and a.port_fixed}
|
|
202
|
+
for app in sorted(apps, key=lambda a: not a.port_fixed):
|
|
203
|
+
if not app.port or app.port_fixed:
|
|
204
|
+
continue
|
|
205
|
+
port = app.port
|
|
206
|
+
while port in taken:
|
|
207
|
+
port += 1
|
|
208
|
+
if port != app.port:
|
|
209
|
+
app.notes.append(f"port {app.port} is taken by another app: runs on {port}")
|
|
210
|
+
app.port = port
|
|
211
|
+
app.port_moved = True
|
|
212
|
+
taken.add(port)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# ── ntc.toml ────────────────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
def _run_command(app):
|
|
218
|
+
manager = app.manager
|
|
219
|
+
run = f"{manager} run {app.script}" if manager != "npm" or app.script != "start" else "npm start"
|
|
220
|
+
if getattr(app, "port_moved", False):
|
|
221
|
+
flag = "--port {port}"
|
|
222
|
+
run += f" -- {flag}" if manager == "npm" else f" {flag}"
|
|
223
|
+
return run
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def entry(app, backend):
|
|
227
|
+
data = {"kind": app.kind, "path": app.folder}
|
|
228
|
+
if app.port:
|
|
229
|
+
data["port"] = app.port
|
|
230
|
+
if app.kind == "django":
|
|
231
|
+
python, python_kind, _ = app.python
|
|
232
|
+
data["python"] = python
|
|
233
|
+
if python_kind == "conda":
|
|
234
|
+
data["python_kind"] = "conda"
|
|
235
|
+
# No `migrate`: an existing project's .env may point at a shared or production database.
|
|
236
|
+
data["setup"] = [f"{{python}} -m pip install -r {app.requirements}"] if app.requirements else []
|
|
237
|
+
data["run"] = "{python} manage.py runserver 127.0.0.1:{port}"
|
|
238
|
+
data["package"] = app.package or "config" # for the commented worker example only
|
|
239
|
+
return data
|
|
240
|
+
if app.kind == "flutter":
|
|
241
|
+
data["setup"] = ["flutter pub get"]
|
|
242
|
+
data["run"] = "flutter run"
|
|
243
|
+
if backend:
|
|
244
|
+
data["needs"] = [backend]
|
|
245
|
+
return data
|
|
246
|
+
if backend:
|
|
247
|
+
data["needs"] = [backend]
|
|
248
|
+
install = {"npm": "npm install --no-audit --no-fund", "pnpm": "pnpm install", "yarn": "yarn install",
|
|
249
|
+
"bun": "bun install"}[app.manager]
|
|
250
|
+
data["setup"] = [install]
|
|
251
|
+
data["run"] = _run_command(app)
|
|
252
|
+
return data
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def run(path=None, assume_yes=False, dry_run=False):
|
|
256
|
+
root = os.path.abspath(path or os.getcwd())
|
|
257
|
+
print(f"\n{Style.BRIGHT}ntc adopt{RESET} {DIM}— {root}{RESET}\n")
|
|
258
|
+
if os.path.exists(os.path.join(root, config.FILENAME)):
|
|
259
|
+
prompts.failure(f"{config.FILENAME} already exists here: this is a workspace. `ntc add` adds apps to it.")
|
|
260
|
+
return False
|
|
261
|
+
started = time.monotonic()
|
|
262
|
+
apps, skipped = detect(root)
|
|
263
|
+
if not apps:
|
|
264
|
+
prompts.failure("No apps found here (looked for manage.py, package.json with a dev/start script, pubspec.yaml).")
|
|
265
|
+
return False
|
|
266
|
+
|
|
267
|
+
width = max(len(a.name) for a in apps)
|
|
268
|
+
folder_width = max(len(a.folder) for a in apps)
|
|
269
|
+
print(f"Found {len(apps)} app{'s' if len(apps) != 1 else ''}:\n")
|
|
270
|
+
for app in apps:
|
|
271
|
+
port = f":{app.port}" if app.port else ""
|
|
272
|
+
extra = app.python[2] if app.kind == "django" else app.manager
|
|
273
|
+
print(f" {prompts.CHECK} {Style.BRIGHT}{app.name.ljust(width)}{RESET} {DIM}{app.folder.ljust(folder_width)}{RESET} "
|
|
274
|
+
f"{config.KINDS[app.kind]['label']:<18} {Fore.CYAN}{port:<6}{RESET} {DIM}{extra}{RESET}")
|
|
275
|
+
for note in app.notes:
|
|
276
|
+
print(f" {DIM}· {note}{RESET}")
|
|
277
|
+
if skipped:
|
|
278
|
+
print(f"\n {DIM}Not apps, left out: {', '.join(skipped)}{RESET}")
|
|
279
|
+
print()
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
for app in apps:
|
|
283
|
+
if app.kind == "django" and len(app.python_options) > 1 and not assume_yes:
|
|
284
|
+
choice = prompts.select(f"Python for {app.name}", [
|
|
285
|
+
(description, i, "") for i, (_, _, description) in enumerate(app.python_options)])
|
|
286
|
+
app.python = app.python_options[choice]
|
|
287
|
+
if dry_run:
|
|
288
|
+
prompts.info("Dry run - nothing was written.")
|
|
289
|
+
return True
|
|
290
|
+
if not assume_yes and not prompts.confirm(f"Write {config.FILENAME}? (nothing in the apps changes)", default=True):
|
|
291
|
+
prompts.info("Nothing was written.")
|
|
292
|
+
return True
|
|
293
|
+
except prompts.Cancelled:
|
|
294
|
+
print(f"\n{DIM}Nothing was written.{RESET}")
|
|
295
|
+
return True
|
|
296
|
+
|
|
297
|
+
backend = next((a.name for a in apps if a.kind == "django"), None)
|
|
298
|
+
entries = {app.name: entry(app, backend) for app in apps}
|
|
299
|
+
name = os.path.basename(root)
|
|
300
|
+
config.write(root, name, entries)
|
|
301
|
+
has_root_git = os.path.isdir(os.path.join(root, ".git"))
|
|
302
|
+
git = "single" if has_root_git else ("per-app" if all(
|
|
303
|
+
os.path.isdir(os.path.join(root, a.folder, ".git")) for a in apps) else "none")
|
|
304
|
+
from ntc.workspace.create import _set_top_level
|
|
305
|
+
_set_top_level(root, "git", git)
|
|
306
|
+
prompts.success(f"{config.FILENAME} written {DIM}({time.monotonic() - started:.1f}s) - edit it to change a port or a command{RESET}")
|
|
307
|
+
|
|
308
|
+
needs_setup = [a.name for a in apps if a.notes and any("not installed" in n or "no Python" in n for n in a.notes)]
|
|
309
|
+
print(f"\n{Style.BRIGHT}Next:{RESET}")
|
|
310
|
+
if needs_setup:
|
|
311
|
+
print(f" {Fore.CYAN}ntc setup{RESET} {DIM}# installs {', '.join(needs_setup)}{RESET}")
|
|
312
|
+
print(f" {Fore.CYAN}ntc start{RESET}\n")
|
|
313
|
+
if backend:
|
|
314
|
+
prompts.info(f"{backend}: setup installs its packages but doesn't migrate - run migrations yourself "
|
|
315
|
+
f"when its .env points where you expect.")
|
|
316
|
+
return True
|