Add LINE icon to website-creator skill
This commit is contained in:
@@ -39,57 +39,62 @@ from urllib.parse import urlparse
|
|||||||
# INTERACTIVE SETUP FUNCTIONS
|
# INTERACTIVE SETUP FUNCTIONS
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
def ask_analytics_setup():
|
def ask_analytics_setup():
|
||||||
"""
|
"""
|
||||||
Interactive analytics setup workflow
|
Interactive analytics setup workflow
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: Analytics configuration
|
dict: Analytics configuration
|
||||||
"""
|
"""
|
||||||
print("\n" + "=" * 60)
|
print("\n" + "=" * 60)
|
||||||
print("📊 ANALYTICS SETUP")
|
print("📊 ANALYTICS SETUP")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
'search_console': None,
|
"search_console": None,
|
||||||
'analytics_type': None, # 'umami' or 'ga4'
|
"analytics_type": None, # 'umami' or 'ga4'
|
||||||
'umami_auto_create': False,
|
"umami_auto_create": False,
|
||||||
'umami_website_id': None,
|
"umami_website_id": None,
|
||||||
'ga4_property_id': None,
|
"ga4_property_id": None,
|
||||||
'ga4_credentials_path': None,
|
"ga4_credentials_path": None,
|
||||||
'ga4_existing': False
|
"ga4_existing": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Step 1: Google Search Console (for all websites)
|
# Step 1: Google Search Console (for all websites)
|
||||||
print("\n1️⃣ Google Search Console Setup")
|
print("\n1️⃣ Google Search Console Setup")
|
||||||
print(" GSC is recommended for all websites for SEO monitoring.")
|
print(" GSC is recommended for all websites for SEO monitoring.")
|
||||||
|
|
||||||
gsc_choice = input("\n Do you want to setup Google Search Console? (y/n): ").strip().lower()
|
gsc_choice = (
|
||||||
|
input("\n Do you want to setup Google Search Console? (y/n): ")
|
||||||
if gsc_choice == 'y':
|
.strip()
|
||||||
|
.lower()
|
||||||
|
)
|
||||||
|
|
||||||
|
if gsc_choice == "y":
|
||||||
print("\n GSC Setup Options:")
|
print("\n GSC Setup Options:")
|
||||||
print(" 1. I'll add it manually later (skip for now)")
|
print(" 1. I'll add it manually later (skip for now)")
|
||||||
print(" 2. I have service account credentials file")
|
print(" 2. I have service account credentials file")
|
||||||
|
|
||||||
gsc_method = input("\n Choose option (1-2): ").strip()
|
gsc_method = input("\n Choose option (1-2): ").strip()
|
||||||
|
|
||||||
if gsc_method == '2':
|
if gsc_method == "2":
|
||||||
gsc_path = input(" Enter path to GSC credentials file: ").strip()
|
gsc_path = input(" Enter path to GSC credentials file: ").strip()
|
||||||
if os.path.exists(gsc_path):
|
if os.path.exists(gsc_path):
|
||||||
config['search_console'] = {
|
config["search_console"] = {
|
||||||
'credentials_path': gsc_path,
|
"credentials_path": gsc_path,
|
||||||
'setup_later': False
|
"setup_later": False,
|
||||||
}
|
}
|
||||||
print(" ✓ GSC credentials loaded")
|
print(" ✓ GSC credentials loaded")
|
||||||
else:
|
else:
|
||||||
print(" ⚠ File not found, will setup later")
|
print(" ⚠ File not found, will setup later")
|
||||||
config['search_console'] = {'setup_later': True}
|
config["search_console"] = {"setup_later": True}
|
||||||
else:
|
else:
|
||||||
config['search_console'] = {'setup_later': True}
|
config["search_console"] = {"setup_later": True}
|
||||||
print(" ✓ Will setup later")
|
print(" ✓ Will setup later")
|
||||||
else:
|
else:
|
||||||
print(" ⏭️ Skipping GSC setup")
|
print(" ⏭️ Skipping GSC setup")
|
||||||
|
|
||||||
# Step 2: Choose Analytics Type (Umami OR GA4)
|
# Step 2: Choose Analytics Type (Umami OR GA4)
|
||||||
print("\n2️⃣ Analytics Platform")
|
print("\n2️⃣ Analytics Platform")
|
||||||
print(" Choose ONE analytics platform:")
|
print(" Choose ONE analytics platform:")
|
||||||
@@ -101,72 +106,84 @@ def ask_analytics_setup():
|
|||||||
print(" - Full-featured analytics")
|
print(" - Full-featured analytics")
|
||||||
print(" - Requires Google account")
|
print(" - Requires Google account")
|
||||||
print(" - Good for existing GA4 users")
|
print(" - Good for existing GA4 users")
|
||||||
|
|
||||||
analytics_choice = input("\n Choose analytics (1-2): ").strip()
|
analytics_choice = input("\n Choose analytics (1-2): ").strip()
|
||||||
|
|
||||||
if analytics_choice == '1':
|
if analytics_choice == "1":
|
||||||
# Umami setup
|
# Umami setup
|
||||||
config['analytics_type'] = 'umami'
|
config["analytics_type"] = "umami"
|
||||||
print("\n 📈 Umami Analytics Setup")
|
print("\n 📈 Umami Analytics Setup")
|
||||||
|
|
||||||
# Check if Umami credentials are configured
|
# Check if Umami credentials are configured
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
load_dotenv(os.path.join(os.path.dirname(__file__), '../../../.env'))
|
|
||||||
|
load_dotenv(os.path.join(os.path.dirname(__file__), "../../../.env"))
|
||||||
umami_url = os.getenv('UMAMI_URL', '')
|
|
||||||
umami_username = os.getenv('UMAMI_USERNAME', '')
|
umami_url = os.getenv("UMAMI_URL", "")
|
||||||
umami_password = os.getenv('UMAMI_PASSWORD', '')
|
umami_username = os.getenv("UMAMI_USERNAME", "")
|
||||||
|
umami_password = os.getenv("UMAMI_PASSWORD", "")
|
||||||
|
|
||||||
if umami_url and umami_username and umami_password:
|
if umami_url and umami_username and umami_password:
|
||||||
print(" ✓ Umami credentials found in .env")
|
print(" ✓ Umami credentials found in .env")
|
||||||
print(" ✓ Will auto-create Umami website for this project")
|
print(" ✓ Will auto-create Umami website for this project")
|
||||||
config['umami_auto_create'] = True
|
config["umami_auto_create"] = True
|
||||||
else:
|
else:
|
||||||
print(" ⚠ Umami credentials not configured in .env")
|
print(" ⚠ Umami credentials not configured in .env")
|
||||||
print(" ⏭️ Skipping Umami setup (can add manually later)")
|
print(" ⏭️ Skipping Umami setup (can add manually later)")
|
||||||
|
|
||||||
elif analytics_choice == '2':
|
elif analytics_choice == "2":
|
||||||
# GA4 setup
|
# GA4 setup
|
||||||
config['analytics_type'] = 'ga4'
|
config["analytics_type"] = "ga4"
|
||||||
print("\n 🔍 Google Analytics 4 Setup")
|
print("\n 🔍 Google Analytics 4 Setup")
|
||||||
print(" 1. Create new GA4 property (auto-setup)")
|
print(" 1. Create new GA4 property (auto-setup)")
|
||||||
print(" 2. Use existing GA4 property (manual setup)")
|
print(" 2. Use existing GA4 property (manual setup)")
|
||||||
|
|
||||||
ga4_choice = input("\n Choose option (1-2): ").strip()
|
ga4_choice = input("\n Choose option (1-2): ").strip()
|
||||||
|
|
||||||
if ga4_choice == '1':
|
if ga4_choice == "1":
|
||||||
print("\n ⚠ Auto-creating GA4 properties requires API setup.")
|
print("\n ⚠ Auto-creating GA4 properties requires API setup.")
|
||||||
print(" ⏭️ Will provide instructions for manual setup")
|
print(" ⏭️ Will provide instructions for manual setup")
|
||||||
config['ga4_existing'] = False
|
config["ga4_existing"] = False
|
||||||
else:
|
else:
|
||||||
print("\n Please provide your existing GA4 details:")
|
print("\n Please provide your existing GA4 details:")
|
||||||
|
|
||||||
# Check unified .env for GA4 credentials
|
# Check unified .env for GA4 credentials
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
load_dotenv(os.path.join(os.path.dirname(__file__), '../../../.env'))
|
|
||||||
|
load_dotenv(os.path.join(os.path.dirname(__file__), "../../../.env"))
|
||||||
ga4_property_id = os.getenv('GA4_PROPERTY_ID', '')
|
|
||||||
ga4_credentials_path = os.getenv('GA4_CREDENTIALS_PATH', '')
|
ga4_property_id = os.getenv("GA4_PROPERTY_ID", "")
|
||||||
|
ga4_credentials_path = os.getenv("GA4_CREDENTIALS_PATH", "")
|
||||||
|
|
||||||
if ga4_property_id:
|
if ga4_property_id:
|
||||||
print(f" Found GA4 Property ID in .env: {ga4_property_id[:20]}...")
|
print(f" Found GA4 Property ID in .env: {ga4_property_id[:20]}...")
|
||||||
use_global = input(" Use this for this project? (y/n): ").strip().lower()
|
use_global = (
|
||||||
|
input(" Use this for this project? (y/n): ").strip().lower()
|
||||||
if use_global == 'y':
|
)
|
||||||
config['ga4_property_id'] = ga4_property_id
|
|
||||||
config['ga4_credentials_path'] = ga4_credentials_path
|
if use_global == "y":
|
||||||
|
config["ga4_property_id"] = ga4_property_id
|
||||||
|
config["ga4_credentials_path"] = ga4_credentials_path
|
||||||
print(" ✓ Using global GA4 credentials")
|
print(" ✓ Using global GA4 credentials")
|
||||||
else:
|
else:
|
||||||
config['ga4_property_id'] = input(" Enter GA4 Property ID: ").strip()
|
config["ga4_property_id"] = input(
|
||||||
config['ga4_credentials_path'] = input(" Enter GA4 credentials file path: ").strip()
|
" Enter GA4 Property ID: "
|
||||||
|
).strip()
|
||||||
|
config["ga4_credentials_path"] = input(
|
||||||
|
" Enter GA4 credentials file path: "
|
||||||
|
).strip()
|
||||||
else:
|
else:
|
||||||
config['ga4_property_id'] = input(" Enter GA4 Property ID (G-XXXXXXXXXX): ").strip()
|
config["ga4_property_id"] = input(
|
||||||
config['ga4_credentials_path'] = input(" Enter GA4 credentials file path: ").strip()
|
" Enter GA4 Property ID (G-XXXXXXXXXX): "
|
||||||
|
).strip()
|
||||||
config['ga4_existing'] = True
|
config["ga4_credentials_path"] = input(
|
||||||
|
" Enter GA4 credentials file path: "
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
config["ga4_existing"] = True
|
||||||
else:
|
else:
|
||||||
print(" ⏭️ Skipping analytics setup")
|
print(" ⏭️ Skipping analytics setup")
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
@@ -237,108 +254,128 @@ PACKAGE_JSON_TEMPLATE = """{{
|
|||||||
# MAIN FUNCTION
|
# MAIN FUNCTION
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main entry point."""
|
"""Main entry point."""
|
||||||
parser = argparse.ArgumentParser(description='Create PDPA-compliant Astro website')
|
parser = argparse.ArgumentParser(description="Create PDPA-compliant Astro website")
|
||||||
parser.add_argument('--name', required=True, help='Website name')
|
parser.add_argument("--name", required=True, help="Website name")
|
||||||
parser.add_argument('--type', default='corporate',
|
parser.add_argument(
|
||||||
choices=['corporate', 'portfolio', 'landing', 'blog', 'ecommerce'],
|
"--type",
|
||||||
help='Website type')
|
default="corporate",
|
||||||
parser.add_argument('--languages', default='th,en',
|
choices=["corporate", "portfolio", "landing", "blog", "ecommerce"],
|
||||||
help='Languages (comma-separated): th, en')
|
help="Website type",
|
||||||
parser.add_argument('--primary-color', default='#2563eb',
|
)
|
||||||
help='Primary color (hex)')
|
parser.add_argument(
|
||||||
parser.add_argument('--secondary-color', default='#1e40af',
|
"--languages", default="th,en", help="Languages (comma-separated): th, en"
|
||||||
help='Secondary color (hex)')
|
)
|
||||||
parser.add_argument('--features', default='blog,contact',
|
parser.add_argument(
|
||||||
help='Features (comma-separated): blog, products, contact, portfolio')
|
"--primary-color", default="#2563eb", help="Primary color (hex)"
|
||||||
parser.add_argument('--umami-id', default='',
|
)
|
||||||
help='Umami Website ID')
|
parser.add_argument(
|
||||||
parser.add_argument('--umami-domain', default='analytics.example.com',
|
"--secondary-color", default="#1e40af", help="Secondary color (hex)"
|
||||||
help='Umami domain')
|
)
|
||||||
parser.add_argument('--output', '-o', default='.',
|
parser.add_argument(
|
||||||
help='Output directory')
|
"--features",
|
||||||
parser.add_argument('--no-interactive', action='store_true',
|
default="blog,contact",
|
||||||
help='Skip interactive setup (use defaults)')
|
help="Features (comma-separated): blog, products, contact, portfolio",
|
||||||
|
)
|
||||||
|
parser.add_argument("--umami-id", default="", help="Umami Website ID")
|
||||||
|
parser.add_argument(
|
||||||
|
"--umami-domain", default="analytics.example.com", help="Umami domain"
|
||||||
|
)
|
||||||
|
parser.add_argument("--output", "-o", default=".", help="Output directory")
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-interactive",
|
||||||
|
action="store_true",
|
||||||
|
help="Skip interactive setup (use defaults)",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Auto-generate admin password from project folder name
|
# Auto-generate admin password from project folder name
|
||||||
args.admin_password = Path(args.output).name.replace(' ', '').lower()
|
args.admin_password = Path(args.output).name.replace(" ", "").lower()
|
||||||
|
|
||||||
# Load unified credentials
|
# Load unified credentials
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
load_dotenv(os.path.join(os.path.dirname(__file__), '../../../.env'))
|
|
||||||
|
load_dotenv(os.path.join(os.path.dirname(__file__), "../../../.env"))
|
||||||
|
|
||||||
# Get Umami credentials for auto-setup
|
# Get Umami credentials for auto-setup
|
||||||
args.umami_url = os.getenv('UMAMI_URL', '')
|
args.umami_url = os.getenv("UMAMI_URL", "")
|
||||||
args.umami_username = os.getenv('UMAMI_USERNAME', '')
|
args.umami_username = os.getenv("UMAMI_USERNAME", "")
|
||||||
args.umami_password = os.getenv('UMAMI_PASSWORD', '')
|
args.umami_password = os.getenv("UMAMI_PASSWORD", "")
|
||||||
args.auto_setup_umami = bool(args.umami_url and args.umami_username and args.umami_password)
|
args.auto_setup_umami = bool(
|
||||||
|
args.umami_url and args.umami_username and args.umami_password
|
||||||
languages = [lang.strip() for lang in args.languages.split(',')]
|
)
|
||||||
default_locale = 'en' if 'en' in languages else languages[0]
|
|
||||||
|
languages = [lang.strip() for lang in args.languages.split(",")]
|
||||||
features = [f.strip() for f in args.features.split(',')]
|
default_locale = "en" if "en" in languages else languages[0]
|
||||||
|
|
||||||
|
features = [f.strip() for f in args.features.split(",")]
|
||||||
|
|
||||||
print(f"Creating website: {args.name}")
|
print(f"Creating website: {args.name}")
|
||||||
print(f"Type: {args.type}")
|
print(f"Type: {args.type}")
|
||||||
print(f"Languages: {languages}")
|
print(f"Languages: {languages}")
|
||||||
print(f"Features: {features}")
|
print(f"Features: {features}")
|
||||||
print(f"Output: {args.output}")
|
print(f"Output: {args.output}")
|
||||||
|
|
||||||
# Interactive analytics setup (if not in no-interactive mode)
|
# Interactive analytics setup (if not in no-interactive mode)
|
||||||
analytics_config = None
|
analytics_config = None
|
||||||
if not args.no_interactive:
|
if not args.no_interactive:
|
||||||
analytics_config = ask_analytics_setup()
|
analytics_config = ask_analytics_setup()
|
||||||
|
|
||||||
# Create project structure
|
# Create project structure
|
||||||
create_project(args, languages, default_locale, features)
|
create_project(args, languages, default_locale, features)
|
||||||
|
|
||||||
# Save analytics configuration to project
|
# Save analytics configuration to project
|
||||||
if analytics_config:
|
if analytics_config:
|
||||||
save_analytics_config(args.output, analytics_config)
|
save_analytics_config(args.output, analytics_config)
|
||||||
|
|
||||||
# Auto-setup Umami if credentials provided
|
# Auto-setup Umami if credentials provided
|
||||||
umami_website_id = args.umami_id
|
umami_website_id = args.umami_id
|
||||||
if args.auto_setup_umami and (not analytics_config or analytics_config.get('analytics_type') == 'umami'):
|
if args.auto_setup_umami and (
|
||||||
|
not analytics_config or analytics_config.get("analytics_type") == "umami"
|
||||||
|
):
|
||||||
print("\n📈 Setting up Umami Analytics...")
|
print("\n📈 Setting up Umami Analytics...")
|
||||||
try:
|
try:
|
||||||
from umami_integration import setup_umami_for_website
|
from umami_integration import setup_umami_for_website
|
||||||
website_domain = args.name.lower().replace(' ', '-') + '.moreminimore.com'
|
|
||||||
|
website_domain = args.name.lower().replace(" ", "-") + ".moreminimore.com"
|
||||||
success, result = setup_umami_for_website(
|
success, result = setup_umami_for_website(
|
||||||
args.umami_url,
|
args.umami_url,
|
||||||
args.umami_username,
|
args.umami_username,
|
||||||
args.umami_password,
|
args.umami_password,
|
||||||
args.name,
|
args.name,
|
||||||
website_domain,
|
website_domain,
|
||||||
args.output
|
args.output,
|
||||||
)
|
)
|
||||||
if success:
|
if success:
|
||||||
umami_website_id = result['website_id']
|
umami_website_id = result["website_id"]
|
||||||
print(f" ✓ Umami website created: {umami_website_id}")
|
print(f" ✓ Umami website created: {umami_website_id}")
|
||||||
else:
|
else:
|
||||||
print(f" ⚠ Umami setup skipped: {result.get('error', 'Unknown error')}")
|
print(
|
||||||
|
f" ⚠ Umami setup skipped: {result.get('error', 'Unknown error')}"
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" ⚠ Umami setup failed: {e}")
|
print(f" ⚠ Umami setup failed: {e}")
|
||||||
print(" Continuing without Umami...")
|
print(" Continuing without Umami...")
|
||||||
|
|
||||||
print(f"\n✅ Website created successfully at: {args.output}")
|
print(f"\n✅ Website created successfully at: {args.output}")
|
||||||
|
|
||||||
# Update .env with Umami ID if auto-setup
|
# Update .env with Umami ID if auto-setup
|
||||||
env_file = os.path.join(args.output, '.env')
|
env_file = os.path.join(args.output, ".env")
|
||||||
if os.path.exists(env_file) and umami_website_id:
|
if os.path.exists(env_file) and umami_website_id:
|
||||||
with open(env_file, 'a', encoding='utf-8') as f:
|
with open(env_file, "a", encoding="utf-8") as f:
|
||||||
f.write(f'\n# Umami Analytics (auto-configured)\n')
|
f.write(f"\n# Umami Analytics (auto-configured)\n")
|
||||||
f.write(f'UMAMI_WEBSITE_ID={umami_website_id}\n')
|
f.write(f"UMAMI_WEBSITE_ID={umami_website_id}\n")
|
||||||
print(f" ✓ Umami ID added to .env")
|
print(f" ✓ Umami ID added to .env")
|
||||||
|
|
||||||
print("\nNext steps:")
|
print("\nNext steps:")
|
||||||
print(f" 1. cd {args.output}")
|
print(f" 1. cd {args.output}")
|
||||||
print(" 2. npm install")
|
print(" 2. npm install")
|
||||||
print(" 3. Update .env with your credentials")
|
print(" 3. Update .env with your credentials")
|
||||||
print(" 4. npm run dev")
|
print(" 4. npm run dev")
|
||||||
|
|
||||||
# Always ask to sync (skip if no-interactive mode)
|
# Always ask to sync (skip if no-interactive mode)
|
||||||
print("")
|
print("")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
@@ -347,39 +384,45 @@ def main():
|
|||||||
print("")
|
print("")
|
||||||
print("Preview at: http://localhost:4321")
|
print("Preview at: http://localhost:4321")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
# Ask if they want to sync to Gitea/Easypanel
|
# Ask if they want to sync to Gitea/Easypanel
|
||||||
if args.no_interactive:
|
if args.no_interactive:
|
||||||
print("✅ Done! Website is ready at:", args.output)
|
print("✅ Done! Website is ready at:", args.output)
|
||||||
print("To sync later, run the sync command manually.")
|
print("To sync later, run the sync command manually.")
|
||||||
return
|
return
|
||||||
|
|
||||||
sync_choice = input("Do you want to sync to Gitea and deploy to Easypanel? (y/n): ").strip().lower()
|
sync_choice = (
|
||||||
|
input("Do you want to sync to Gitea and deploy to Easypanel? (y/n): ")
|
||||||
if sync_choice != 'y':
|
.strip()
|
||||||
|
.lower()
|
||||||
|
)
|
||||||
|
|
||||||
|
if sync_choice != "y":
|
||||||
print("")
|
print("")
|
||||||
print("✅ Done! Website is ready at:", args.output)
|
print("✅ Done! Website is ready at:", args.output)
|
||||||
print("To sync later, run this script again or use gitea-sync/easypanel-deploy skills.")
|
print(
|
||||||
|
"To sync later, run this script again or use gitea-sync/easypanel-deploy skills."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
print("Proceeding with sync and deployment...")
|
print("Proceeding with sync and deployment...")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
# Step 1: Sync to Gitea
|
# Step 1: Sync to Gitea
|
||||||
print("📦 Step 1/3: Syncing to Gitea...")
|
print("📦 Step 1/3: Syncing to Gitea...")
|
||||||
git_url = sync_to_gitea(args.output, args.name)
|
git_url = sync_to_gitea(args.output, args.name)
|
||||||
|
|
||||||
# Step 2: Deploy to Easypanel
|
# Step 2: Deploy to Easypanel
|
||||||
print("")
|
print("")
|
||||||
print("🚀 Step 2/3: Deploying to Easypanel...")
|
print("🚀 Step 2/3: Deploying to Easypanel...")
|
||||||
deployment_url = deploy_to_easypanel(args.output, args.name, git_url)
|
deployment_url = deploy_to_easypanel(args.output, args.name, git_url)
|
||||||
|
|
||||||
# Step 3: Verify and monitor
|
# Step 3: Verify and monitor
|
||||||
print("")
|
print("")
|
||||||
print("📊 Step 3/3: Monitoring deployment...")
|
print("📊 Step 3/3: Monitoring deployment...")
|
||||||
monitor_deployment(args.name)
|
monitor_deployment(args.name)
|
||||||
|
|
||||||
# Final output
|
# Final output
|
||||||
print("")
|
print("")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
@@ -399,32 +442,40 @@ def main():
|
|||||||
|
|
||||||
def save_analytics_config(output_path: str, config: dict):
|
def save_analytics_config(output_path: str, config: dict):
|
||||||
"""Save analytics configuration to project context"""
|
"""Save analytics configuration to project context"""
|
||||||
context_dir = os.path.join(output_path, 'context')
|
context_dir = os.path.join(output_path, "context")
|
||||||
os.makedirs(context_dir, exist_ok=True)
|
os.makedirs(context_dir, exist_ok=True)
|
||||||
|
|
||||||
# Save data-services.json
|
# Save data-services.json
|
||||||
data_services = {
|
data_services = {
|
||||||
'ga4': {
|
"ga4": {
|
||||||
'enabled': config.get('analytics_type') == 'ga4',
|
"enabled": config.get("analytics_type") == "ga4",
|
||||||
'property_id': config.get('ga4_property_id', ''),
|
"property_id": config.get("ga4_property_id", ""),
|
||||||
'credentials_path': config.get('ga4_credentials_path', '')
|
"credentials_path": config.get("ga4_credentials_path", ""),
|
||||||
} if config.get('analytics_type') == 'ga4' else {'enabled': False},
|
}
|
||||||
'gsc': {
|
if config.get("analytics_type") == "ga4"
|
||||||
'enabled': config.get('search_console') is not None,
|
else {"enabled": False},
|
||||||
'site_url': '',
|
"gsc": {
|
||||||
'credentials_path': config.get('search_console', {}).get('credentials_path', '')
|
"enabled": config.get("search_console") is not None,
|
||||||
|
"site_url": "",
|
||||||
|
"credentials_path": config.get("search_console", {}).get(
|
||||||
|
"credentials_path", ""
|
||||||
|
),
|
||||||
},
|
},
|
||||||
'umami': {
|
"umami": {
|
||||||
'enabled': config.get('analytics_type') == 'umami',
|
"enabled": config.get("analytics_type") == "umami",
|
||||||
'api_url': os.getenv('UMAMI_URL', ''),
|
"api_url": os.getenv("UMAMI_URL", ""),
|
||||||
'website_id': config.get('umami_website_id', '')
|
"website_id": config.get("umami_website_id", ""),
|
||||||
} if config.get('analytics_type') == 'umami' else {'enabled': False},
|
}
|
||||||
'dataforseo': {'enabled': False}
|
if config.get("analytics_type") == "umami"
|
||||||
|
else {"enabled": False},
|
||||||
|
"dataforseo": {"enabled": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
with open(os.path.join(context_dir, 'data-services.json'), 'w', encoding='utf-8') as f:
|
with open(
|
||||||
|
os.path.join(context_dir, "data-services.json"), "w", encoding="utf-8"
|
||||||
|
) as f:
|
||||||
json.dump(data_services, f, indent=2)
|
json.dump(data_services, f, indent=2)
|
||||||
|
|
||||||
print(f" ✓ Analytics config saved to context/data-services.json")
|
print(f" ✓ Analytics config saved to context/data-services.json")
|
||||||
|
|
||||||
|
|
||||||
@@ -432,87 +483,109 @@ def save_analytics_config(output_path: str, config: dict):
|
|||||||
# PROJECT CREATION FUNCTIONS
|
# PROJECT CREATION FUNCTIONS
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
def create_project(args, languages, default_locale, features):
|
def create_project(args, languages, default_locale, features):
|
||||||
"""Create the Astro project structure with templates."""
|
"""Create the Astro project structure with templates."""
|
||||||
output_path = Path(args.output)
|
output_path = Path(args.output)
|
||||||
project_name = args.name.lower().replace(' ', '-')
|
project_name = args.name.lower().replace(" ", "-")
|
||||||
site_url = f"https://{project_name}.moreminimore.com"
|
site_url = f"https://{project_name}.moreminimore.com"
|
||||||
|
|
||||||
# Get template directory
|
# Get template directory
|
||||||
script_dir = Path(__file__).parent
|
script_dir = Path(__file__).parent
|
||||||
template_dir = script_dir / 'templates'
|
template_dir = script_dir / "templates"
|
||||||
|
|
||||||
print("\n📁 Creating project structure...")
|
print("\n📁 Creating project structure...")
|
||||||
|
|
||||||
# Create directories
|
# Create directories
|
||||||
dirs = [
|
dirs = [
|
||||||
output_path / 'public' / 'images',
|
output_path / "public" / "images",
|
||||||
output_path / 'src' / 'components' / 'common',
|
output_path / "public" / "images" / "icons",
|
||||||
output_path / 'src' / 'components' / 'consent',
|
output_path / "src" / "components" / "common",
|
||||||
output_path / 'src' / 'components' / 'ui',
|
output_path / "src" / "components" / "consent",
|
||||||
output_path / 'src' / 'layouts',
|
output_path / "src" / "components" / "ui",
|
||||||
output_path / 'src' / 'pages',
|
output_path / "src" / "layouts",
|
||||||
output_path / 'src' / 'pages' / default_locale,
|
output_path / "src" / "pages",
|
||||||
output_path / 'src' / 'styles',
|
output_path / "src" / "pages" / default_locale,
|
||||||
output_path / 'src' / 'content' / 'blog',
|
output_path / "src" / "styles",
|
||||||
output_path / 'src' / 'lib',
|
output_path / "src" / "content" / "blog",
|
||||||
output_path / 'db',
|
output_path / "src" / "lib",
|
||||||
|
output_path / "db",
|
||||||
]
|
]
|
||||||
|
|
||||||
for d in dirs:
|
for d in dirs:
|
||||||
d.mkdir(parents=True, exist_ok=True)
|
d.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
print(" ✓ Directory structure created")
|
print(" ✓ Directory structure created")
|
||||||
|
|
||||||
# Copy templates if they exist
|
# Copy templates if they exist
|
||||||
if template_dir.exists():
|
if template_dir.exists():
|
||||||
print(" 📦 Copying templates with IDs...")
|
print(" 📦 Copying templates with IDs...")
|
||||||
|
|
||||||
# Copy layouts
|
# Copy layouts
|
||||||
layout_src = template_dir / 'layouts' / 'BaseLayout.astro'
|
layout_src = template_dir / "layouts" / "BaseLayout.astro"
|
||||||
if layout_src.exists():
|
if layout_src.exists():
|
||||||
content = layout_src.read_text(encoding='utf-8')
|
content = layout_src.read_text(encoding="utf-8")
|
||||||
content = content.replace("const siteName = 'Website Name'", f"const siteName = '{args.name}'")
|
content = content.replace(
|
||||||
content = content.replace("const siteUrl = 'https://example.com'", f"const siteUrl = '{site_url}'")
|
"const siteName = 'Website Name'", f"const siteName = '{args.name}'"
|
||||||
(output_path / 'src' / 'layouts' / 'BaseLayout.astro').write_text(content, encoding='utf-8')
|
)
|
||||||
|
content = content.replace(
|
||||||
|
"const siteUrl = 'https://example.com'", f"const siteUrl = '{site_url}'"
|
||||||
|
)
|
||||||
|
(output_path / "src" / "layouts" / "BaseLayout.astro").write_text(
|
||||||
|
content, encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
# Copy Header
|
# Copy Header
|
||||||
header_src = template_dir / 'components' / 'common' / 'Header.astro'
|
header_src = template_dir / "components" / "common" / "Header.astro"
|
||||||
if header_src.exists():
|
if header_src.exists():
|
||||||
shutil.copy(header_src, output_path / 'src' / 'components' / 'common' / 'Header.astro')
|
shutil.copy(
|
||||||
|
header_src,
|
||||||
|
output_path / "src" / "components" / "common" / "Header.astro",
|
||||||
|
)
|
||||||
|
|
||||||
# Copy Footer
|
# Copy Footer
|
||||||
footer_src = template_dir / 'components' / 'common' / 'Footer.astro'
|
footer_src = template_dir / "components" / "common" / "Footer.astro"
|
||||||
if footer_src.exists():
|
if footer_src.exists():
|
||||||
shutil.copy(footer_src, output_path / 'src' / 'components' / 'common' / 'Footer.astro')
|
shutil.copy(
|
||||||
|
footer_src,
|
||||||
|
output_path / "src" / "components" / "common" / "Footer.astro",
|
||||||
|
)
|
||||||
|
|
||||||
# Copy page templates
|
# Copy page templates
|
||||||
page_src = template_dir / 'pages' / 'index.astro'
|
page_src = template_dir / "pages" / "index.astro"
|
||||||
if page_src.exists():
|
if page_src.exists():
|
||||||
shutil.copy(page_src, output_path / 'src' / 'pages' / default_locale / 'index.astro')
|
shutil.copy(
|
||||||
|
page_src, output_path / "src" / "pages" / default_locale / "index.astro"
|
||||||
|
)
|
||||||
|
|
||||||
# Copy styles
|
# Copy styles
|
||||||
style_src = template_dir / 'styles' / 'global.css'
|
style_src = template_dir / "styles" / "global.css"
|
||||||
if style_src.exists():
|
if style_src.exists():
|
||||||
shutil.copy(style_src, output_path / 'src' / 'styles' / 'global.css')
|
shutil.copy(style_src, output_path / "src" / "styles" / "global.css")
|
||||||
|
|
||||||
|
# Copy LINE icon
|
||||||
|
line_icon_src = template_dir / "icons" / "line.svg"
|
||||||
|
if line_icon_src.exists():
|
||||||
|
icons_dir = output_path / "public" / "images" / "icons"
|
||||||
|
icons_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy(line_icon_src, icons_dir / "line.svg")
|
||||||
|
print(" ✓ LINE icon copied")
|
||||||
|
|
||||||
print(" ✓ Templates copied")
|
print(" ✓ Templates copied")
|
||||||
|
|
||||||
# Create astro.config.mjs
|
# Create astro.config.mjs
|
||||||
locales_str = ', '.join([f"'{lang}'" for lang in languages])
|
locales_str = ", ".join([f"'{lang}'" for lang in languages])
|
||||||
astro_config = ASTRO_CONFIG_TEMPLATE.format(
|
astro_config = ASTRO_CONFIG_TEMPLATE.format(
|
||||||
site_url=site_url,
|
site_url=site_url, locales=locales_str, default_locale=default_locale
|
||||||
locales=locales_str,
|
|
||||||
default_locale=default_locale
|
|
||||||
)
|
)
|
||||||
(output_path / 'astro.config.mjs').write_text(astro_config, encoding='utf-8')
|
(output_path / "astro.config.mjs").write_text(astro_config, encoding="utf-8")
|
||||||
print(" ✓ astro.config.mjs created")
|
print(" ✓ astro.config.mjs created")
|
||||||
|
|
||||||
# Create package.json
|
# Create package.json
|
||||||
package_json = PACKAGE_JSON_TEMPLATE.format(name=project_name)
|
package_json = PACKAGE_JSON_TEMPLATE.format(name=project_name)
|
||||||
(output_path / 'package.json').write_text(package_json, encoding='utf-8')
|
(output_path / "package.json").write_text(package_json, encoding="utf-8")
|
||||||
print(" ✓ package.json created")
|
print(" ✓ package.json created")
|
||||||
|
|
||||||
# Create tsconfig.json
|
# Create tsconfig.json
|
||||||
tsconfig = """{
|
tsconfig = """{
|
||||||
"extends": "astro/tsconfigs/strict",
|
"extends": "astro/tsconfigs/strict",
|
||||||
@@ -524,8 +597,8 @@ def create_project(args, languages, default_locale, features):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
(output_path / 'tsconfig.json').write_text(tsconfig, encoding='utf-8')
|
(output_path / "tsconfig.json").write_text(tsconfig, encoding="utf-8")
|
||||||
|
|
||||||
# Create env file
|
# Create env file
|
||||||
env_content = f"""# Website Configuration
|
env_content = f"""# Website Configuration
|
||||||
SITE_NAME={args.name}
|
SITE_NAME={args.name}
|
||||||
@@ -535,11 +608,11 @@ SITE_URL={site_url}
|
|||||||
# UMAMI_WEBSITE_ID=
|
# UMAMI_WEBSITE_ID=
|
||||||
# UMAMI_URL=
|
# UMAMI_URL=
|
||||||
"""
|
"""
|
||||||
(output_path / '.env').write_text(env_content, encoding='utf-8')
|
(output_path / ".env").write_text(env_content, encoding="utf-8")
|
||||||
print(" ✓ Configuration files created")
|
print(" ✓ Configuration files created")
|
||||||
|
|
||||||
# Create basic index page if no template
|
# Create basic index page if no template
|
||||||
if not (output_path / 'src' / 'pages' / default_locale / 'index.astro').exists():
|
if not (output_path / "src" / "pages" / default_locale / "index.astro").exists():
|
||||||
index_content = f"""---
|
index_content = f"""---
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
import Header from '../components/common/Header.astro';
|
import Header from '../components/common/Header.astro';
|
||||||
@@ -557,10 +630,12 @@ import Footer from '../components/common/Footer.astro';
|
|||||||
<Footer />
|
<Footer />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
"""
|
"""
|
||||||
(output_path / 'src' / 'pages' / default_locale / 'index.astro').write_text(index_content, encoding='utf-8')
|
(output_path / "src" / "pages" / default_locale / "index.astro").write_text(
|
||||||
|
index_content, encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
print(" ✓ Basic pages created")
|
print(" ✓ Basic pages created")
|
||||||
|
|
||||||
# Create Dockerfile
|
# Create Dockerfile
|
||||||
dockerfile = f"""FROM node:20-slim
|
dockerfile = f"""FROM node:20-slim
|
||||||
|
|
||||||
@@ -580,9 +655,9 @@ RUN npm run build
|
|||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
CMD ["npm", "run", "preview"]
|
CMD ["npm", "run", "preview"]
|
||||||
"""
|
"""
|
||||||
(output_path / 'Dockerfile').write_text(dockerfile, encoding='utf-8')
|
(output_path / "Dockerfile").write_text(dockerfile, encoding="utf-8")
|
||||||
print(" ✓ Dockerfile created")
|
print(" ✓ Dockerfile created")
|
||||||
|
|
||||||
# Create .gitignore
|
# Create .gitignore
|
||||||
gitignore = """# Dependencies
|
gitignore = """# Dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
@@ -605,9 +680,9 @@ dist/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
"""
|
"""
|
||||||
(output_path / '.gitignore').write_text(gitignore, encoding='utf-8')
|
(output_path / ".gitignore").write_text(gitignore, encoding="utf-8")
|
||||||
print(" ✓ .gitignore created")
|
print(" ✓ .gitignore created")
|
||||||
|
|
||||||
return output_path
|
return output_path
|
||||||
|
|
||||||
|
|
||||||
@@ -615,18 +690,20 @@ def sync_to_gitea(output_path: str, repo_name: str) -> str:
|
|||||||
"""Sync project to Gitea repository."""
|
"""Sync project to Gitea repository."""
|
||||||
try:
|
try:
|
||||||
# Import gitea sync functionality
|
# Import gitea sync functionality
|
||||||
sys.path.insert(0, str(Path(__file__).parent.parent / 'gitea-sync' / 'scripts'))
|
sys.path.insert(0, str(Path(__file__).parent.parent / "gitea-sync" / "scripts"))
|
||||||
from sync import sync_repo
|
from sync import sync_repo
|
||||||
|
|
||||||
# Use the gitea-sync script
|
# Use the gitea-sync script
|
||||||
result = sync_repo(
|
result = sync_repo(
|
||||||
repo_name=repo_name,
|
repo_name=repo_name,
|
||||||
repo_path=output_path,
|
repo_path=output_path,
|
||||||
description=f"Website: {repo_name}",
|
description=f"Website: {repo_name}",
|
||||||
auto_push=True
|
auto_push=True,
|
||||||
)
|
)
|
||||||
if result.get('success'):
|
if result.get("success"):
|
||||||
return result.get('url', f"https://git.moreminimore.com/user/{repo_name}.git")
|
return result.get(
|
||||||
|
"url", f"https://git.moreminimore.com/user/{repo_name}.git"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
print(f" ⚠ Gitea sync failed: {result.get('error')}")
|
print(f" ⚠ Gitea sync failed: {result.get('error')}")
|
||||||
return f"https://git.moreminimore.com/user/{repo_name}.git"
|
return f"https://git.moreminimore.com/user/{repo_name}.git"
|
||||||
@@ -641,44 +718,46 @@ def deploy_to_easypanel(output_path: str, project_name: str, git_url: str) -> st
|
|||||||
"""Deploy project to Easypanel."""
|
"""Deploy project to Easypanel."""
|
||||||
try:
|
try:
|
||||||
# Import easypanel deploy functionality
|
# Import easypanel deploy functionality
|
||||||
sys.path.insert(0, str(Path(__file__).parent.parent / 'easypanel-deploy' / 'scripts'))
|
sys.path.insert(
|
||||||
|
0, str(Path(__file__).parent.parent / "easypanel-deploy" / "scripts")
|
||||||
|
)
|
||||||
from deploy import (
|
from deploy import (
|
||||||
get_session_token,
|
get_session_token,
|
||||||
create_service,
|
create_service,
|
||||||
update_git_source,
|
update_git_source,
|
||||||
update_build_type,
|
update_build_type,
|
||||||
deploy_service,
|
deploy_service,
|
||||||
load_env
|
load_env,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Load credentials
|
# Load credentials
|
||||||
env = load_env()
|
env = load_env()
|
||||||
username = env.get('EASYPANEL_USERNAME', '')
|
username = env.get("EASYPANEL_USERNAME", "")
|
||||||
password = env.get('EASYPANEL_PASSWORD', '')
|
password = env.get("EASYPANEL_PASSWORD", "")
|
||||||
|
|
||||||
if not username or not password:
|
if not username or not password:
|
||||||
print(" ⚠ Easypanel credentials not found")
|
print(" ⚠ Easypanel credentials not found")
|
||||||
print(" Skipping deployment - you can deploy manually later")
|
print(" Skipping deployment - you can deploy manually later")
|
||||||
return f"https://{project_name}.moreminimore.com"
|
return f"https://{project_name}.moreminimore.com"
|
||||||
|
|
||||||
# Get session token
|
# Get session token
|
||||||
token = get_session_token(username, password)
|
token = get_session_token(username, password)
|
||||||
if not token:
|
if not token:
|
||||||
print(" ⚠ Failed to get Easypanel session")
|
print(" ⚠ Failed to get Easypanel session")
|
||||||
return f"https://{project_name}.moreminimore.com"
|
return f"https://{project_name}.moreminimore.com"
|
||||||
|
|
||||||
# Create service
|
# Create service
|
||||||
create_service(project_name, 'web', token)
|
create_service(project_name, "web", token)
|
||||||
|
|
||||||
# Update git source
|
# Update git source
|
||||||
update_git_source(project_name, 'web', git_url, 'main', token)
|
update_git_source(project_name, "web", git_url, "main", token)
|
||||||
|
|
||||||
# Set build type to dockerfile
|
# Set build type to dockerfile
|
||||||
update_build_type(project_name, 'web', token, 'dockerfile')
|
update_build_type(project_name, "web", token, "dockerfile")
|
||||||
|
|
||||||
# Deploy
|
# Deploy
|
||||||
deploy_service(project_name, 'web', token)
|
deploy_service(project_name, "web", token)
|
||||||
|
|
||||||
return f"https://{project_name}.moreminimore.com"
|
return f"https://{project_name}.moreminimore.com"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" ⚠ Easypanel deployment error: {e}")
|
print(f" ⚠ Easypanel deployment error: {e}")
|
||||||
@@ -693,5 +772,5 @@ def monitor_deployment(project_name: str):
|
|||||||
print(" Check status at: https://panelwebsite.moreminimore.com")
|
print(" Check status at: https://panelwebsite.moreminimore.com")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ const legalLinks = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const socialLinks = [
|
const socialLinks = [
|
||||||
{ name: 'Facebook', href: 'https://facebook.com', icon: 'facebook' },
|
{ name: 'Facebook', href: 'https://facebook.com', icon: 'facebook', svg: '' },
|
||||||
{ name: 'Line', href: 'https://line.me', icon: 'line' },
|
{ name: 'Line', href: 'https://line.me', icon: 'line', svg: 'line' },
|
||||||
{ name: 'YouTube', href: 'https://youtube.com', icon: 'youtube' },
|
{ name: 'YouTube', href: 'https://youtube.com', icon: 'youtube', svg: '' },
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -45,9 +45,13 @@ const socialLinks = [
|
|||||||
<!-- Social Links -->
|
<!-- Social Links -->
|
||||||
<div id="footer-social">
|
<div id="footer-social">
|
||||||
<div id="social-links-container" class="flex space-x-4">
|
<div id="social-links-container" class="flex space-x-4">
|
||||||
{socialLinks.map((social, index) => (
|
{socialLinks.map((social) => (
|
||||||
<a id={`social-${social.icon}`} href={social.href} target="_blank" rel="noopener noreferrer" class="w-10 h-10 bg-secondary-800 rounded-full flex items-center justify-center hover:bg-primary-600 transition-colors" aria-label={social.name}>
|
<a id={`social-${social.icon}`} href={social.href} target="_blank" rel="noopener noreferrer" class="w-10 h-10 bg-secondary-800 rounded-full flex items-center justify-center hover:bg-primary-600 transition-colors" aria-label={social.name}>
|
||||||
<span class="text-sm font-medium">{social.name[0]}</span>
|
{social.svg === 'line' ? (
|
||||||
|
<img src="/images/icons/line.svg" alt="LINE" class="w-5 h-5" />
|
||||||
|
) : (
|
||||||
|
<span class="text-sm font-medium">{social.name[0]}</span>
|
||||||
|
)}
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
10
skills/website-creator/scripts/templates/icons/line.svg
Normal file
10
skills/website-creator/scripts/templates/icons/line.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 36 36" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,-6,-6)">
|
||||||
|
<path d="M12.5,42L35.5,42C39.09,42 42,39.09 42,35.5L42,12.5C42,8.91 39.09,6 35.5,6L12.5,6C8.91,6 6,8.91 6,12.5L6,35.5C6,39.09 8.91,42 12.5,42Z" style="fill:rgb(0,195,0);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-6,-6)">
|
||||||
|
<path d="M37.113,22.417C37.113,16.552 31.233,11.78 24.006,11.78C16.779,11.78 10.898,16.552 10.898,22.417C10.898,27.675 15.561,32.079 21.86,32.912C22.287,33.004 22.868,33.194 23.015,33.558C23.147,33.889 23.101,34.408 23.057,34.743C23.057,34.743 22.904,35.668 22.87,35.865C22.813,36.196 22.607,37.161 24.005,36.572C25.404,35.983 31.553,32.127 34.303,28.961L34.302,28.961C36.203,26.879 37.113,24.764 37.113,22.417ZM18.875,25.907L16.271,25.907C15.892,25.907 15.584,25.599 15.584,25.219L15.584,20.01C15.584,19.631 15.892,19.323 16.271,19.323C16.65,19.323 16.958,19.631 16.958,20.01L16.958,24.531L18.875,24.531C19.254,24.531 19.562,24.839 19.562,25.218C19.562,25.598 19.254,25.907 18.875,25.907ZM21.568,25.219C21.568,25.598 21.26,25.907 20.881,25.907C20.502,25.907 20.194,25.599 20.194,25.219L20.194,20.01C20.194,19.631 20.502,19.323 20.881,19.323C21.26,19.323 21.568,19.631 21.568,20.01L21.568,25.219ZM27.838,25.219C27.838,25.516 27.65,25.778 27.368,25.871C27.297,25.895 27.223,25.907 27.15,25.907C26.935,25.907 26.73,25.804 26.601,25.632L23.932,21.997L23.932,25.219C23.932,25.598 23.624,25.907 23.244,25.907C22.865,25.907 22.556,25.599 22.556,25.219L22.556,20.01C22.556,19.714 22.745,19.452 23.026,19.358C23.097,19.334 23.17,19.323 23.244,19.323C23.458,19.323 23.664,19.426 23.793,19.598L26.463,23.233L26.463,20.01C26.463,19.631 26.772,19.323 27.151,19.323C27.53,19.323 27.838,19.631 27.838,20.01L27.838,25.219ZM32.052,21.927C32.431,21.927 32.74,22.235 32.74,22.615C32.74,22.994 32.432,23.302 32.052,23.302L30.135,23.302L30.135,24.532L32.052,24.532C32.431,24.532 32.74,24.84 32.74,25.219C32.74,25.598 32.431,25.907 32.052,25.907L29.448,25.907C29.07,25.907 28.761,25.599 28.761,25.219L28.761,20.011C28.761,19.632 29.069,19.324 29.448,19.324L32.052,19.324C32.431,19.324 32.74,19.632 32.74,20.011C32.74,20.39 32.432,20.698 32.052,20.698L30.135,20.698L30.135,21.928L32.052,21.928L32.052,21.927Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
Reference in New Issue
Block a user