[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/QueryaHub/Querya-Desktop/main/patch_theme.py [Back]  [Original]

import os

with open('lib/core/theme/theme_registry_service.dart', 'r') as f:
    content = f.read()

# 1. Imports
target_imports = """import 'theme_metadata.dart';
import 'theme_paths.dart';"""
new_imports = """import 'theme_metadata.dart';
import 'theme_paths.dart';
import '../extensions/local_extension_registry.dart';
import '../extensions/extension_paths.dart';
import '../extensions/models/extension_manifest.dart';
import '../extensions/models/extension_type.dart';"""
content = content.replace(target_imports, new_imports)

# 2. Add flag
target_flag = """  final List _bundledThemeAssetFiles;
  final _ThemeLruCache _themeCache;
  int _themeParseCount = 0;"""
new_flag = """  final List _bundledThemeAssetFiles;
  final _ThemeLruCache _themeCache;
  int _themeParseCount = 0;
  bool _hasMigratedThemes = false;"""
content = content.replace(target_flag, new_flag)

# 3. loadThemeDefinitions
target_load = """  Future loadThemeDefinitions() async {
    final definitions = [];

    await _loadBuiltinAssetDefinitions(definitions);

    await _scanDirectory(
      await _userThemesDirectory(),
      ThemeSource.filesystem,
      definitions,
    );
    await _scanDirectory(
      await _importedThemesDirectory(),
      ThemeSource.imported,
      definitions,
    );

    final legacy = await _legacyImportedDefinition();
    if (legacy != null) {
      definitions.removeWhere(
        (definition) =>
            definition.path == legacy.path &&
            definition.source != ThemeSource.legacyImported,
      );
      definitions.add(legacy);
    }

    definitions.sort(
      (a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()),
    );
    return List.unmodifiable(definitions);
  }"""
new_load = """  Future loadThemeDefinitions() async {
    final definitions = [];

    await _loadBuiltinAssetDefinitions(definitions);

    if (!_hasMigratedThemes) {
      await _migrateLegacyThemesToExtensions();
      _hasMigratedThemes = true;
    }

    await LocalExtensionRegistry.instance.load();
    for (final manifest in LocalExtensionRegistry.instance.manifests) {
      if (manifest.type != ExtensionType.theme) continue;
      final installPath = manifest.installPath;
      final mainFile = manifest.main;
      if (installPath == null || mainFile == null) continue;

      final file = File(p.join(installPath, mainFile));
      if (!await file.exists()) continue;

      final definition = await _definitionFromFile(file, ThemeSource.filesystem);
      if (definition != null) {
        definitions.add(definition);
      }
    }

    final legacy = await _legacyImportedDefinition();
    if (legacy != null) {
      definitions.removeWhere(
        (definition) =>
            definition.path == legacy.path &&
            definition.source != ThemeSource.legacyImported,
      );
      definitions.add(legacy);
    }

    definitions.sort(
      (a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()),
    );
    return List.unmodifiable(definitions);
  }

  Future _migrateLegacyThemesToExtensions() async {
    final dirs = [
      await _userThemesDirectory(),
      await _importedThemesDirectory(),
    ];
    
    final extensionsDir = await ExtensionPaths.ensureExtensionsDirectory();

    for (final directory in dirs) {
      if (!await directory.exists()) continue;
      
      await for (final entity in directory.list(followLinks: false)) {
        if (entity is! File) continue;
        if (p.basename(entity.path) == ThemeImportService.storedFileName) continue;
        final ext = p.extension(entity.path).toLowerCase();
        if (ext != '.json' && ext != '.jsonc') continue;

        try {
          final definition = await _definitionFromFile(entity, ThemeSource.filesystem);
          if (definition == null) continue;

          final slug = ThemeImportService.slugifyThemeName(definition.name);
          var finalExtDir = Directory(p.join(extensionsDir.path, slug));
          var counter = 2;
          while (await finalExtDir.exists()) {
            finalExtDir = Directory(p.join(extensionsDir.path, '$slug-$counter'));
            counter++;
          }
          await finalExtDir.create(recursive: true);

          final themeFile = File(p.join(finalExtDir.path, 'theme.json'));
          await entity.copy(themeFile.path);

          final manifest = ExtensionManifest(
            id: definition.id,
            name: definition.name,
            version: '1.0.0',
            publisher: 'Unknown',
            type: ExtensionType.theme,
            engines: const {'querya_desktop': '*'},
            main: 'theme.json',
            description: 'Migrated custom theme',
          );
          
          final manifestFile = File(p.join(finalExtDir.path, 'manifest.json'));
          await manifestFile.writeAsString(jsonEncode(manifest.toJson()));

          await entity.delete(); // Delete old file to complete migration
        } catch (_) {}
      }
    }
    // Reload extensions since we might have added new ones
    await LocalExtensionRegistry.instance.reload();
  }"""
content = content.replace(target_load, new_load)

# 4. importThemeFile
target_import = """  /// Validates [sourcePath], copies into the user themes directory, and returns
  /// the scanned [ThemeDefinition].
  Future importThemeFile(String sourcePath) async {
    try {
      final source = File(sourcePath);
      if (!await source.exists()) {
        return const ThemeDefinitionImportFailure('Theme file not found.');
      }

      final raw = await source.readAsString();
      final hash = _contentHash(raw);
      final json = _decodeRoot(raw);
      if (json == null) {
        return const ThemeDefinitionImportFailure('Invalid JSON.');
      }

      final themesDir = await _userThemesDirectory();
      if (!await themesDir.exists()) {
        await themesDir.create(recursive: true);
      }

      final schema = json['schema']?.toString();
      late final String logicalId;
      late final String preferredBaseName;
      late String contentToWrite;

      if (schema == queryaThemeSchemaV1) {
        final manifest = QueryaThemeManifest.fromJsonString(raw);
        logicalId = manifest.id;
        preferredBaseName = ThemeImportService.safeThemeFileBase(manifest.id);
        contentToWrite = raw;
      } else {
        final manifest = VsCodeThemeManifest.fromJsonString(raw);
        if (manifest.colors.isEmpty) {
          return const ThemeDefinitionImportFailure(
            'Theme file has no "colors" section to import.',
          );
        }
        final displayName = manifest.name?.trim().isNotEmpty == true
            ? manifest.name!.trim()
            : p.basenameWithoutExtension(sourcePath);
        preferredBaseName = ThemeImportService.slugifyThemeName(displayName);
        logicalId = preferredBaseName;
        contentToWrite = raw;
      }

      var resolved = await _resolveImportDestination(
        themesDir: themesDir,
        hash: hash,
        logicalId: logicalId,
        preferredBaseName: preferredBaseName,
      );

      if (!resolved.reused &&
          schema == queryaThemeSchemaV1 &&
          resolved.renamedId != null) {
        contentToWrite = _rewriteCustomThemeId(raw, resolved.renamedId!);
      }

      if (!resolved.reused) {
        await resolved.file.writeAsString(contentToWrite);
      }

      final definition =
          await _definitionFromFile(resolved.file, ThemeSource.filesystem);
      if (definition == null) {
        return const ThemeDefinitionImportFailure(
          'Failed to index imported theme.',
        );
      }

      return ThemeDefinitionImportSuccess(
        definition: definition,
        reusedExisting: resolved.reused,
      );
    } on QueryaThemeManifestParseException catch (e) {
      return ThemeDefinitionImportFailure(e.message);
    } on VsCodeThemeParseException catch (e) {
      return ThemeDefinitionImportFailure(e.message);
    } on IOException catch (e) {
      return ThemeDefinitionImportFailure(e.toString());
    } on Object catch (e) {
      return ThemeDefinitionImportFailure(e.toString());
    }
  }"""

new_import = """  /// Validates [sourcePath], creates an extension directory, and returns
  /// the scanned [ThemeDefinition].
  Future importThemeFile(String sourcePath) async {
    try {
      final source = File(sourcePath);
      if (!await source.exists()) {
        return const ThemeDefinitionImportFailure('Theme file not found.');
      }

      final raw = await source.readAsString();
      final json = _decodeRoot(raw);
      if (json == null) {
        return const ThemeDefinitionImportFailure('Invalid JSON.');
      }

      final extensionsDir = await ExtensionPaths.ensureExtensionsDirectory();

      final schema = json['schema']?.toString();
      late final String logicalId;
      late final String preferredBaseName;
      late String contentToWrite;

      if (schema == queryaThemeSchemaV1) {
        final manifest = QueryaThemeManifest.fromJsonString(raw);
        logicalId = manifest.id;
        preferredBaseName = ThemeImportService.safeThemeFileBase(manifest.id);
        contentToWrite = raw;
      } else {
        final manifest = VsCodeThemeManifest.fromJsonString(raw);
        if (manifest.colors.isEmpty) {
          return const ThemeDefinitionImportFailure(
            'Theme file has no "colors" section to import.',
          );
        }
        final displayName = manifest.name?.trim().isNotEmpty == true
            ? manifest.name!.trim()
            : p.basenameWithoutExtension(sourcePath);
        preferredBaseName = ThemeImportService.slugifyThemeName(displayName);
        logicalId = preferredBaseName;
        contentToWrite = raw;
      }

      await LocalExtensionRegistry.instance.load();
      bool reused = false;
      String? existingInstallPath;
      
      for (final extManifest in LocalExtensionRegistry.instance.manifests) {
        if (extManifest.type == ExtensionType.theme && extManifest.id == logicalId) {
           reused = true;
           existingInstallPath = extManifest.installPath;
           break;
        }
      }

      File resolvedFile;
      if (reused && existingInstallPath != null) {
         resolvedFile = File(p.join(existingInstallPath, 'theme.json'));
      } else {
         var finalExtDir = Directory(p.join(extensionsDir.path, preferredBaseName));
         var counter = 2;
         while (await finalExtDir.exists()) {
           finalExtDir = Directory(p.join(extensionsDir.path, '$preferredBaseName-$counter'));
           counter++;
         }
         await finalExtDir.create(recursive: true);
         
         resolvedFile = File(p.join(finalExtDir.path, 'theme.json'));
         await resolvedFile.writeAsString(contentToWrite);

         final newManifest = ExtensionManifest(
            id: logicalId,
            name: preferredBaseName,
            version: '1.0.0',
            publisher: 'Unknown',
            type: ExtensionType.theme,
            engines: const {'querya_desktop': '*'},
            main: 'theme.json',
            description: 'Imported custom theme',
         );
         final manifestFile = File(p.join(finalExtDir.path, 'manifest.json'));
         await manifestFile.writeAsString(jsonEncode(newManifest.toJson()));
         
         await LocalExtensionRegistry.instance.reload();
      }

      final definition = await _definitionFromFile(resolvedFile, ThemeSource.filesystem);
      if (definition == null) {
        return const ThemeDefinitionImportFailure('Failed to index imported theme.');
      }

      return ThemeDefinitionImportSuccess(
        definition: definition,
        reusedExisting: reused,
      );
    } on QueryaThemeManifestParseException catch (e) {
      return ThemeDefinitionImportFailure(e.message);
    } on VsCodeThemeParseException catch (e) {
      return ThemeDefinitionImportFailure(e.message);
    } on IOException catch (e) {
      return ThemeDefinitionImportFailure(e.toString());
    } on Object catch (e) {
      return ThemeDefinitionImportFailure(e.toString());
    }
  }"""
content = content.replace(target_import, new_import)

# 5. Remove unused methods using python block extracting instead of regex
# Or just simple replace
import re
to_remove = [
    (r'  Future _scanDirectory\(', r'^\s*\}\s*$', 3),
    (r'  Future _resolveImportDestination\(', r'^\s*\}\s*$', 3),
    (r'  Future _nextRenamedThemeId\(', r'^\s*\}\s*$', 3),
    (r'  Future _themeIdExists\(', r'^\s*\}\s*$', 3),
    (r'  Future _nextAvailableThemeFile\(', r'^\s*\}\s*$', 3),
    (r'  String _rewriteCustomThemeId\(', r'^\s*\}\s*$', 3),
    (r'class _ResolvedImportDestination \{', r'^\}\s*$', 0),
]

lines = content.split('\n')
for start_regex, end_regex, indent in to_remove:
    start_idx = -1
    end_idx = -1
    for i, line in enumerate(lines):
        if re.search(start_regex, line):
            start_idx = i
            break
    if start_idx != -1:
        # find matching closing brace
        brace_count = 0
        started = False
        for i in range(start_idx, len(lines)):
            brace_count += lines[i].count('{')
            brace_count -= lines[i].count('}')
            if '{' in lines[i]:
                started = True
            if started and brace_count == 0:
                end_idx = i
                break
        if end_idx != -1:
            lines[start_idx:end_idx+1] = []

content = '\n'.join(lines)

with open('lib/core/theme/theme_registry_service.dart', 'w') as f:
    f.write(content)

print("Patch applied successfully.")

Web Proxy Viewer  |  New URL  |  Original Page