arcane 6.1.1 copy "arcane: ^6.1.1" to clipboard
arcane: ^6.1.1 copied to clipboard

A modified variant of shadcn_flutter along with new features & improvements. This package follows the changes of shadcn_flutter while also maintaining additional features & fixes.

example/lib/main.dart

import 'dart:async';
import 'dart:convert';
import 'dart:math';

import 'package:arcane/arcane.dart';
import 'package:flutter/services.dart';
import 'package:gui_shape/geo/geo.dart';
import 'package:gui_shape/gui/gui.dart';

bool v = false;
String? vv;
void main() {
  runZonedGuarded(() {
    runApp("example", const ExampleArcaneApp());
  }, (error, stackTrace) {
    print("Error: $error");
    print("Stack: $stackTrace");
  });
}

class ExampleArcaneApp extends StatelessWidget {
  const ExampleArcaneApp({super.key});

  void didShortcut() {
    print("YOU DID IT");
  }

  @override
  Widget build(BuildContext context) => ArcaneShortcuts(
          shortcuts: {
            LogicalKeySet(
              LogicalKeyboardKey.control,
              LogicalKeyboardKey.alt,
              LogicalKeyboardKey.keyF,
            ): didShortcut
          },
          child: const ArcaneApp(
              home: HomeScreen(),
              showPerformanceOverlay: false,
              theme: ArcaneTheme(
                  themeMode: ThemeMode.system,
                  blurMode: ArcaneBlurMode.backdropFilter)));
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) => FillScreen(
        gutter: false,
        child: Center(
          child: EncodedStrand(
            b64: "fX0C7ZfPQg==",
            base: 3,
          ),
        ),
      );
}

class EncodedStrand extends StatelessWidget {
  final String b64;
  final int offset;
  final int base;

  const EncodedStrand(
      {super.key, required this.b64, this.offset = 0, this.base = 3});

  @override
  Widget build(BuildContext context) {
    String baseB = base64ToBaseN(b64, base);

    String ch = charsets["hebrew"]!;
    String encoded = "$ch${encodeToCustomBase(b64, ch)}";

    String extractedCharset = extractCharset(encoded);
    String redecoded = decodeFromCustomBase(
        encoded.substring(extractedCharset.length), extractedCharset);

    print(b64);
    print(encoded);
    print("CH = $extractedCharset");
    print(redecoded);
    print(ch.length);

    List<Widget> shapes = [];
    for (int i = 0; i < baseB.length; i++) {
      int n = int.parse(baseB[i]) + offset;
      shapes.add(NShape(sides: n));
    }

    return Wrap(
      runAlignment: WrapAlignment.start,
      alignment: WrapAlignment.start,
      crossAxisAlignment: WrapCrossAlignment.start,
      children: shapes,
    ).padHorizontal(32);
  }
}

class NShape extends StatelessWidget {
  final int sides;

  const NShape({super.key, this.sides = 3});

  @override
  Widget build(BuildContext context) => true
      ? Icon(getThing(sides))
      : SizedBox(
          width: 12,
          height: 12,
          child: Transform.rotate(
            angle: 0,
            child: GuiClipShape(
              shape: GuiShapePolygon(sides: sides, startAngle: GeoAngle.zero),
              child: Container(
                color: Colors.white,
              ),
            ),
          ),
        );
}

String base64ToBaseN(String base64Str, int n) {
  if (n < 2 || n > 10) {
    throw ArgumentError('Base n must be between 2 and 10');
  }

  // Decode base64 to bytes
  Uint8List bytes = base64.decode(base64Str);

  // Convert bytes to BigInt
  BigInt num = BigInt.zero;
  for (int byte in bytes) {
    num = (num << 8) | BigInt.from(byte);
  }

  // Handle zero
  if (num == BigInt.zero) {
    return '0';
  }

  // Convert BigInt to base n
  StringBuffer sb = StringBuffer();
  while (num > BigInt.zero) {
    BigInt rem = num % BigInt.from(n);
    sb.write(rem.toInt());
    num = num ~/ BigInt.from(n);
  }

  // Reverse the string to get correct order
  String result = sb.toString().split('').reversed.join();

  return result;
}

IconData getThing(int x) => switch (x) {
      0 => Icons.circle,
      1 => Icons.triangle,
      2 => Icons.diamond,
      3 => Icons.x,
      _ => Icons.x
    };

///////

String _c(int from, [int? to, Set<int>? not]) => to == null
    ? String.fromCharCode(from)
    : List.generate(to - from, (i) => from + i)
        .where((c) => not == null || !not.contains(c))
        .map((i) => String.fromCharCode(i))
        .join();

final Map<String, String> charsets = {
  "gurmukhi": _c(0x0a01, 0x0a76, {
    0x0a04,
    0x0a0b,
    0x0a0c,
    0x0a0d,
    0x0a0e,
    0x0a11,
    0x0a12,
    0x0a29,
    0x0a31,
    0x0a34,
    0x0a37,
    0x0a3a,
    0x0a3b,
    0x0a3d,
    0x0a43,
    0x0a44,
    0x0a45,
    0x0a46,
    0x0a49,
    0x0a4a,
    0x0a4e,
    0x0a4f,
    0x0a50,
    0x0a52,
    0x0a53,
    0x0a54,
    0x0a55,
    0x0a56,
    0x0a57,
    0x0a58,
    0x0a5d,
    0x0a5f,
    0x0a60,
    0x0a61,
    0x0a62,
    0x0a63,
    0x0a64,
    0x0a65,
  }),
  "gujarati": _c(0x0a81, 0x0aff, {
    0x0a84,
    0x0a8e,
    0x0a92,
    0x0aa9,
    0x0ab1,
    0x0ab4,
    0x0aba,
    0x0abb,
    0x0ac6,
    0x0aca,
    0x0ace,
    0x0acf,
    0x0ad1,
    0x0ad2,
    0x0ad3,
    0x0ad4,
    0x0ad5,
    0x0ad6,
    0x0ad7,
    0x0ad8,
    0x0ad9,
    0x0ada,
    0x0adb,
    0x0adc,
    0x0add,
    0x0ade,
    0x0adf,
    0x0ae4,
    0x0ae5,
    0x0af2,
    0x0af3,
    0x0af4,
    0x0af5,
    0x0af6,
    0x0af7,
    0x0af8,
  }),
  "devanagari": _c(0x0900, 0x097f),
  "nko": _c(0x07c0, 0x07ff, {0x07fc}),
  "syriac": "${_c(0x0710, 0x072f)}${_c(0x074d, 0x074f)}",
  "thaana": _c(0x0780, 0x07b1),
  "arabic": "${_c(0x0600, 0x06ff, {
        0x061A,
        0x061D,
        0x653
      })}${_c(0x0750, 0x077f)}${_c(0x08a0, 0x08bd)}",
  "arabicLine": "${_c(0x0600, 0x0605)}${_c(0x060e)}",
  "latin": _c(0x00c0, 0x02af),
  "greek": _c(0x0388, 0x03ff),
  "cyrrilic": _c(0x0400, 0x052f, {
    0x0483,
    0x0484,
    0x0485,
    0x0486,
    0x0487,
  }),
  "hebrew": _c(0x05d0, 0x05f2, {0x05ec, 0x05ed, 0x05ee, 0x05ef, 0x05eb}),
  "armenian": _c(0x0531, 0x0588, {
    0x0557,
    0x0558,
    0x0560,
    0x0559,
    0x055a,
    0x055b,
    0x055c,
    0x055d,
    0x055e,
    0x055f,
  }),
  "bengali": _c(0x0980, 0x09fe, {
    0x09D0,
    0x09D8,
    0x09CA,
    0x0992,
    0x09C5,
    0x09D4,
    0x09B1,
    0x09B2,
    0x09B3,
    0x09BA,
    0x09C6,
    0x09B5,
    0x09C2,
    0x09C3,
    0x098E,
    0x09B4,
    0x0984,
    0x098d,
    0x0991,
    0x09a9,
    0x09bb,
    0x09c9,
    0x09cf,
    0x09d1,
    0x09d2,
    0x09d3,
    0x09d5,
    0x09d6,
    0x09d9,
    0x09da,
    0x09db,
    0x09de,
    0x09e4,
    0x09e5,
  }),
};

extension XString on String {
  String genAsCharset(int length) =>
      List.generate(length, (index) => this[Random().nextInt(this.length)])
          .join();
}

enum KeyMode { rancid, bars, regular }

String genKey({KeyMode keyMode = KeyMode.rancid}) {
  if (keyMode == KeyMode.bars) {
    return "iIlLjJ1".genAsCharset(16);
  } else if (keyMode == KeyMode.regular) {
    return "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"
        .genAsCharset(32);
  }

  Random r = Random();
  int length = 128;
  String buf = "";

  while (buf.length <= length) {
    String charset =
        charsets[charsets.keys.toList()[r.nextInt(charsets.length)]]!;
    int c = r.nextInt(3);

    if (c == 0) {
      charset = charset.toLowerCase();
    } else if (c == 1) {
      charset = charset.toUpperCase();
    }

    buf += charset.genAsCharset(r.nextInt(8) + 4);
  }

  return buf.substring(0, length);
}

String generateSparkAPIKey() {
  return "$sparkKeyHeader(${genKey(keyMode: KeyMode.regular)})";
}

String generateMageAPIKey() {
  return "$mageKeyHeader(${genKey(keyMode: KeyMode.regular)})";
}

const sparkKeyHeader = "sprk";
const mageKeyHeader = "mage";

String encodeToCustomBase(String base64Input, String charset) {
  if (base64Input.isEmpty) return '';

  List<int> bytes = base64.decode(base64Input);
  int n = charset.length;
  if (n < 2) throw Exception('Charset must have at least 2 unique characters');

  BigInt number = BigInt.zero;
  for (int byte in bytes) {
    number = (number << 8) | BigInt.from(byte & 0xFF);
  }

  int bitLength = bytes.length * 8;
  if (bitLength == 0) return '';

  double log2N = log(n) / log(2);
  int outputLength = (bitLength / log2N).ceil().toInt();

  List<int> digits = [];
  BigInt base = BigInt.from(n);

  while (number > BigInt.zero) {
    digits.add((number % base).toInt());
    number = number ~/ base;
  }

  while (digits.length < outputLength) {
    digits.add(0);
  }

  digits = digits.reversed.toList();

  StringBuffer sb = StringBuffer();
  for (int digit in digits) {
    sb.write(charset[digit]);
  }

  return sb.toString();
}

String extractCharset(String encoded) {
  List<String> x = [];
  for (int i = 0; i < encoded.length; i++) {
    if (!x.contains(encoded[i])) {
      x.add(encoded[i]);
    } else {
      break;
    }
  }

  return x.join();
}

String decodeFromCustomBase(String encoded, String charset) {
  if (encoded.isEmpty) return '';

  int outputLength = encoded.length;
  int n = charset.length;
  if (n < 2) throw Exception('Charset must have at least 2 unique characters');

  Map<String, int> charToDigit = {};
  for (int i = 0; i < n; i++) {
    String ch = charset[i];
    if (charToDigit.containsKey(ch))
      throw Exception('Charset must have unique characters');
    charToDigit[ch] = i;
  }

  BigInt number = BigInt.zero;
  BigInt base = BigInt.from(n);
  for (int i = 0; i < outputLength; i++) {
    String ch = encoded[i];
    int? digit = charToDigit[ch];
    if (digit == null) throw Exception('Invalid character in encoded string');
    number = number * base + BigInt.from(digit);
  }

  double log2N = log(n) / log(2);
  double maxBits = outputLength * log2N;
  double minBits = (outputLength - 1) * log2N;

  int byteLen = (maxBits / 8).floor();
  int bitLen = byteLen * 8;
  if (!(bitLen > minBits && bitLen <= maxBits)) {
    throw Exception('Invalid encoded length for this charset');
  }

  List<int> bytes = List<int>.filled(byteLen, 0);
  BigInt temp = number;
  for (int i = byteLen - 1; i >= 0; i--) {
    bytes[i] = (temp & BigInt.from(0xFF)).toInt();
    temp = temp >> 8;
  }
  if (temp != BigInt.zero) {
    throw Exception('Number too large for the computed byte length');
  }

  return base64.encode(bytes);
}