cataract 0.1.1 → 0.1.2
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +0 -29
- data/.github/workflows/docs.yml +51 -0
- data/.gitignore +2 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +1 -0
- data/README.md +1 -1
- data/Rakefile +3 -2
- data/docs/files/EXAMPLE.md +35 -0
- data/examples/css_analyzer/analyzer.rb +12 -29
- data/examples/css_analyzer.rb +0 -7
- data/ext/cataract/cataract.c +19 -16
- data/ext/cataract/cataract.h +4 -0
- data/ext/cataract/extconf.rb +1 -1
- data/ext/cataract/merge.c +731 -59
- data/ext/cataract/shorthand_expander.c +152 -39
- data/ext/cataract_color/color_conversion.c +59 -28
- data/ext/cataract_color/color_conversion_named.c +10 -0
- data/lib/cataract/declarations.rb +1 -2
- data/lib/cataract/version.rb +1 -1
- data/lib/cataract.rb +3 -3
- metadata +3 -1
|
@@ -498,6 +498,8 @@ VALUE cataract_expand_list_style(VALUE self, VALUE value) {
|
|
|
498
498
|
* This is complex - background has many sub-properties and / separator for size
|
|
499
499
|
*/
|
|
500
500
|
VALUE cataract_expand_background(VALUE self, VALUE value) {
|
|
501
|
+
DEBUG_PRINTF("[cataract_expand_background] input value: '%s'\n", RSTRING_PTR(value));
|
|
502
|
+
|
|
501
503
|
// First, check if there's a / separator for background-size
|
|
502
504
|
const char *str = StringValueCStr(value);
|
|
503
505
|
const char *slash = strchr(str, '/');
|
|
@@ -529,16 +531,25 @@ VALUE cataract_expand_background(VALUE self, VALUE value) {
|
|
|
529
531
|
const char *attachment_keywords[] = {"scroll", "fixed", NULL};
|
|
530
532
|
const char *position_keywords[] = {"left", "right", "top", "bottom", "center", NULL};
|
|
531
533
|
|
|
532
|
-
VALUE color = Qnil,
|
|
534
|
+
VALUE color = Qnil, repeat = Qnil, attachment = Qnil, size = size_part;
|
|
533
535
|
VALUE position_parts = rb_ary_new(); // Collect all position keywords
|
|
536
|
+
VALUE image_parts = rb_ary_new(); // Collect all image functions (for layered backgrounds)
|
|
534
537
|
|
|
535
538
|
for (long i = 0; i < len; i++) {
|
|
536
539
|
VALUE part = rb_ary_entry(parts, i);
|
|
537
540
|
const char *str = StringValueCStr(part);
|
|
538
541
|
|
|
539
|
-
// Check for image
|
|
540
|
-
if (
|
|
541
|
-
|
|
542
|
+
// Check for image (url, gradient functions, or none) - collect ALL image tokens
|
|
543
|
+
if (strncmp(str, "url(", 4) == 0 ||
|
|
544
|
+
strncmp(str, "linear-gradient(", 16) == 0 ||
|
|
545
|
+
strncmp(str, "radial-gradient(", 16) == 0 ||
|
|
546
|
+
strncmp(str, "repeating-linear-gradient(", 26) == 0 ||
|
|
547
|
+
strncmp(str, "repeating-radial-gradient(", 26) == 0 ||
|
|
548
|
+
strncmp(str, "conic-gradient(", 15) == 0 ||
|
|
549
|
+
strcmp(str, "none") == 0) {
|
|
550
|
+
DEBUG_PRINTF(" -> Recognized as IMAGE: '%s'\n", str);
|
|
551
|
+
rb_ary_push(image_parts, part);
|
|
552
|
+
goto next_part;
|
|
542
553
|
}
|
|
543
554
|
// Check for repeat
|
|
544
555
|
else if (repeat == Qnil) {
|
|
@@ -570,10 +581,12 @@ VALUE cataract_expand_background(VALUE self, VALUE value) {
|
|
|
570
581
|
// Check for color (hex, rgb, or keyword)
|
|
571
582
|
if (color == Qnil) {
|
|
572
583
|
if (str[0] == '#' || strncmp(str, "rgb", 3) == 0 || strncmp(str, "hsl", 3) == 0) {
|
|
584
|
+
DEBUG_PRINTF(" -> Recognized as COLOR (function/hex): '%s'\n", str);
|
|
573
585
|
color = part;
|
|
574
586
|
} else {
|
|
575
587
|
for (int j = 0; color_keywords[j]; j++) {
|
|
576
588
|
if (strcmp(str, color_keywords[j]) == 0) {
|
|
589
|
+
DEBUG_PRINTF(" -> Recognized as COLOR (keyword): '%s'\n", str);
|
|
577
590
|
color = part;
|
|
578
591
|
break;
|
|
579
592
|
}
|
|
@@ -590,12 +603,32 @@ VALUE cataract_expand_background(VALUE self, VALUE value) {
|
|
|
590
603
|
position = rb_ary_join(position_parts, STR_NEW_CSTR(" "));
|
|
591
604
|
}
|
|
592
605
|
|
|
593
|
-
if
|
|
594
|
-
|
|
595
|
-
if (
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
606
|
+
// Join all image parts into a single string if any were found (for layered backgrounds)
|
|
607
|
+
VALUE image = Qnil;
|
|
608
|
+
if (RARRAY_LEN(image_parts) > 0) {
|
|
609
|
+
image = rb_ary_join(image_parts, STR_NEW_CSTR(" "));
|
|
610
|
+
DEBUG_PRINTF(" -> Joined %ld image parts into: '%s'\n", RARRAY_LEN(image_parts), RSTRING_PTR(image));
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
DEBUG_PRINTF("[cataract_expand_background] Final values:\n");
|
|
614
|
+
DEBUG_PRINTF(" color: %s\n", color != Qnil ? RSTRING_PTR(color) : "(nil -> transparent)");
|
|
615
|
+
DEBUG_PRINTF(" image: %s\n", image != Qnil ? RSTRING_PTR(image) : "(nil -> none)");
|
|
616
|
+
|
|
617
|
+
// Background shorthand sets ALL longhand properties
|
|
618
|
+
// Unspecified values get CSS initial values (defaults)
|
|
619
|
+
rb_hash_aset(result, STR_NEW_CSTR("background-color"),
|
|
620
|
+
color != Qnil ? color : STR_NEW_CSTR("transparent"));
|
|
621
|
+
rb_hash_aset(result, STR_NEW_CSTR("background-image"),
|
|
622
|
+
image != Qnil ? image : STR_NEW_CSTR("none"));
|
|
623
|
+
rb_hash_aset(result, STR_NEW_CSTR("background-repeat"),
|
|
624
|
+
repeat != Qnil ? repeat : STR_NEW_CSTR("repeat"));
|
|
625
|
+
rb_hash_aset(result, STR_NEW_CSTR("background-attachment"),
|
|
626
|
+
attachment != Qnil ? attachment : STR_NEW_CSTR("scroll"));
|
|
627
|
+
rb_hash_aset(result, STR_NEW_CSTR("background-position"),
|
|
628
|
+
position != Qnil ? position : STR_NEW_CSTR("0% 0%"));
|
|
629
|
+
if (size != Qnil) {
|
|
630
|
+
rb_hash_aset(result, STR_NEW_CSTR("background-size"), size);
|
|
631
|
+
}
|
|
599
632
|
|
|
600
633
|
return result;
|
|
601
634
|
}
|
|
@@ -783,48 +816,100 @@ VALUE cataract_create_background_shorthand(VALUE self, VALUE properties) {
|
|
|
783
816
|
VALUE image = rb_hash_aref(properties, STR_NEW_CSTR("background-image"));
|
|
784
817
|
VALUE repeat = rb_hash_aref(properties, STR_NEW_CSTR("background-repeat"));
|
|
785
818
|
VALUE position = rb_hash_aref(properties, STR_NEW_CSTR("background-position"));
|
|
819
|
+
VALUE attachment = rb_hash_aref(properties, STR_NEW_CSTR("background-attachment"));
|
|
786
820
|
VALUE size = rb_hash_aref(properties, STR_NEW_CSTR("background-size"));
|
|
787
821
|
|
|
788
822
|
// Need at least one property
|
|
789
|
-
if (NIL_P(color) && NIL_P(image) && NIL_P(repeat) && NIL_P(position) && NIL_P(size)) {
|
|
823
|
+
if (NIL_P(color) && NIL_P(image) && NIL_P(repeat) && NIL_P(position) && NIL_P(attachment) && NIL_P(size)) {
|
|
790
824
|
return Qnil;
|
|
791
825
|
}
|
|
792
826
|
|
|
827
|
+
// Check if all 5 core properties are present (from shorthand expansion)
|
|
828
|
+
// If so, omit defaults to optimize output
|
|
829
|
+
int all_present = !NIL_P(color) && !NIL_P(image) && !NIL_P(repeat) &&
|
|
830
|
+
!NIL_P(position) && !NIL_P(attachment);
|
|
831
|
+
|
|
832
|
+
DEBUG_PRINTF("[create_background_shorthand] all_present=%d (color=%d image=%d repeat=%d pos=%d attach=%d)\n",
|
|
833
|
+
all_present, !NIL_P(color), !NIL_P(image), !NIL_P(repeat),
|
|
834
|
+
!NIL_P(position), !NIL_P(attachment));
|
|
835
|
+
if (!NIL_P(color)) DEBUG_PRINTF(" color='%s'\n", RSTRING_PTR(color));
|
|
836
|
+
if (!NIL_P(image)) DEBUG_PRINTF(" image='%s'\n", RSTRING_PTR(image));
|
|
837
|
+
if (!NIL_P(repeat)) DEBUG_PRINTF(" repeat='%s'\n", RSTRING_PTR(repeat));
|
|
838
|
+
if (!NIL_P(position)) DEBUG_PRINTF(" position='%s'\n", RSTRING_PTR(position));
|
|
839
|
+
if (!NIL_P(attachment)) DEBUG_PRINTF(" attachment='%s'\n", RSTRING_PTR(attachment));
|
|
840
|
+
|
|
793
841
|
VALUE result = STR_NEW_WITH_CAPACITY(128);
|
|
794
842
|
int first = 1;
|
|
795
843
|
|
|
796
844
|
if (!NIL_P(color)) {
|
|
797
|
-
|
|
798
|
-
|
|
845
|
+
// Omit default 'transparent' if all properties present
|
|
846
|
+
if (!all_present || !STR_EQ(color, "transparent")) {
|
|
847
|
+
DEBUG_PRINTF(" -> Adding color: '%s'\n", RSTRING_PTR(color));
|
|
848
|
+
rb_str_append(result, color);
|
|
849
|
+
first = 0;
|
|
850
|
+
} else {
|
|
851
|
+
DEBUG_PRINTF(" -> Omitting default color 'transparent'\n");
|
|
852
|
+
}
|
|
799
853
|
}
|
|
800
854
|
if (!NIL_P(image)) {
|
|
801
|
-
if
|
|
802
|
-
|
|
803
|
-
|
|
855
|
+
// Omit default 'none' if all properties present
|
|
856
|
+
if (!all_present || !STR_EQ(image, "none")) {
|
|
857
|
+
DEBUG_PRINTF(" -> Adding image: '%s'\n", RSTRING_PTR(image));
|
|
858
|
+
if (!first) rb_str_cat2(result, " ");
|
|
859
|
+
rb_str_append(result, image);
|
|
860
|
+
first = 0;
|
|
861
|
+
} else {
|
|
862
|
+
DEBUG_PRINTF(" -> Omitting default image 'none'\n");
|
|
863
|
+
}
|
|
804
864
|
}
|
|
805
865
|
if (!NIL_P(repeat)) {
|
|
806
|
-
if
|
|
807
|
-
|
|
808
|
-
|
|
866
|
+
// Omit default 'repeat' if all properties present
|
|
867
|
+
if (!all_present || !STR_EQ(repeat, "repeat")) {
|
|
868
|
+
DEBUG_PRINTF(" -> Adding repeat: '%s'\n", RSTRING_PTR(repeat));
|
|
869
|
+
if (!first) rb_str_cat2(result, " ");
|
|
870
|
+
rb_str_append(result, repeat);
|
|
871
|
+
first = 0;
|
|
872
|
+
} else {
|
|
873
|
+
DEBUG_PRINTF(" -> Omitting default repeat 'repeat'\n");
|
|
874
|
+
}
|
|
809
875
|
}
|
|
810
876
|
if (!NIL_P(position)) {
|
|
811
|
-
if
|
|
812
|
-
|
|
813
|
-
|
|
877
|
+
// Omit default '0% 0%' if all properties present
|
|
878
|
+
if (!all_present || !STR_EQ(position, "0% 0%")) {
|
|
879
|
+
DEBUG_PRINTF(" -> Adding position: '%s'\n", RSTRING_PTR(position));
|
|
880
|
+
if (!first) rb_str_cat2(result, " ");
|
|
881
|
+
rb_str_append(result, position);
|
|
882
|
+
first = 0;
|
|
883
|
+
} else {
|
|
884
|
+
DEBUG_PRINTF(" -> Omitting default position '0%% 0%%'\n");
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
if (!NIL_P(attachment)) {
|
|
888
|
+
// Omit default 'scroll' if all properties present
|
|
889
|
+
if (!all_present || !STR_EQ(attachment, "scroll")) {
|
|
890
|
+
DEBUG_PRINTF(" -> Adding attachment: '%s'\n", RSTRING_PTR(attachment));
|
|
891
|
+
if (!first) rb_str_cat2(result, " ");
|
|
892
|
+
rb_str_append(result, attachment);
|
|
893
|
+
first = 0;
|
|
894
|
+
} else {
|
|
895
|
+
DEBUG_PRINTF(" -> Omitting default attachment 'scroll'\n");
|
|
896
|
+
}
|
|
814
897
|
}
|
|
815
898
|
if (!NIL_P(size)) {
|
|
816
899
|
// size needs to be prefixed with /
|
|
900
|
+
DEBUG_PRINTF(" -> Adding size: '/%s'\n", RSTRING_PTR(size));
|
|
817
901
|
if (!first) rb_str_cat2(result, " ");
|
|
818
|
-
rb_str_cat2(result, "/
|
|
902
|
+
rb_str_cat2(result, "/");
|
|
819
903
|
rb_str_append(result, size);
|
|
820
904
|
}
|
|
821
905
|
|
|
906
|
+
DEBUG_PRINTF("[create_background_shorthand] result='%s'\n", RSTRING_PTR(result));
|
|
822
907
|
return result;
|
|
823
908
|
}
|
|
824
909
|
|
|
825
910
|
// Create font shorthand from longhand properties
|
|
826
911
|
// Requires: font-size and font-family
|
|
827
|
-
// Optional: font-style, font-weight, line-height
|
|
912
|
+
// Optional: font-style, font-variant, font-weight, line-height
|
|
828
913
|
VALUE cataract_create_font_shorthand(VALUE self, VALUE properties) {
|
|
829
914
|
VALUE size = rb_hash_aref(properties, STR_NEW_CSTR("font-size"));
|
|
830
915
|
VALUE family = rb_hash_aref(properties, STR_NEW_CSTR("font-family"));
|
|
@@ -835,32 +920,60 @@ VALUE cataract_create_font_shorthand(VALUE self, VALUE properties) {
|
|
|
835
920
|
}
|
|
836
921
|
|
|
837
922
|
VALUE style = rb_hash_aref(properties, STR_NEW_CSTR("font-style"));
|
|
923
|
+
VALUE variant = rb_hash_aref(properties, STR_NEW_CSTR("font-variant"));
|
|
838
924
|
VALUE weight = rb_hash_aref(properties, STR_NEW_CSTR("font-weight"));
|
|
839
925
|
VALUE line_height = rb_hash_aref(properties, STR_NEW_CSTR("line-height"));
|
|
840
926
|
|
|
927
|
+
// Check if all optional properties are present (from shorthand expansion)
|
|
928
|
+
// If so, omit defaults to optimize output
|
|
929
|
+
int all_present = !NIL_P(style) && !NIL_P(variant) && !NIL_P(weight) && !NIL_P(line_height);
|
|
930
|
+
|
|
841
931
|
VALUE result = STR_NEW_WITH_CAPACITY(128);
|
|
842
932
|
int has_content = 0;
|
|
843
933
|
|
|
844
|
-
// Order: style weight size/line-height family
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
934
|
+
// Order: style variant weight size/line-height family
|
|
935
|
+
if (!NIL_P(style)) {
|
|
936
|
+
// Omit default 'normal' only if all properties present
|
|
937
|
+
if (!all_present || !STR_EQ(style, "normal")) {
|
|
938
|
+
rb_str_append(result, style);
|
|
939
|
+
has_content = 1;
|
|
940
|
+
}
|
|
849
941
|
}
|
|
850
|
-
if (!NIL_P(
|
|
851
|
-
if
|
|
852
|
-
|
|
853
|
-
|
|
942
|
+
if (!NIL_P(variant)) {
|
|
943
|
+
// Omit default 'normal' only if all properties present
|
|
944
|
+
if (!all_present || !STR_EQ(variant, "normal")) {
|
|
945
|
+
if (has_content) rb_str_cat2(result, " ");
|
|
946
|
+
rb_str_append(result, variant);
|
|
947
|
+
has_content = 1;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
if (!NIL_P(weight)) {
|
|
951
|
+
// Omit default 'normal' only if all properties present
|
|
952
|
+
if (!all_present || !STR_EQ(weight, "normal")) {
|
|
953
|
+
if (has_content) rb_str_cat2(result, " ");
|
|
954
|
+
rb_str_append(result, weight);
|
|
955
|
+
has_content = 1;
|
|
956
|
+
}
|
|
854
957
|
}
|
|
855
958
|
|
|
856
959
|
// size is required
|
|
857
960
|
if (has_content) rb_str_cat2(result, " ");
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
961
|
+
if (all_present && !NIL_P(line_height)) {
|
|
962
|
+
// Omit line-height if default 'normal' and all properties present
|
|
963
|
+
if (!STR_EQ(line_height, "normal")) {
|
|
964
|
+
rb_str_append(result, size);
|
|
965
|
+
rb_str_cat2(result, "/");
|
|
966
|
+
rb_str_append(result, line_height);
|
|
967
|
+
} else {
|
|
968
|
+
rb_str_append(result, size);
|
|
969
|
+
}
|
|
970
|
+
} else {
|
|
971
|
+
rb_str_append(result, size);
|
|
972
|
+
// Include line-height if present (partial set)
|
|
973
|
+
if (!NIL_P(line_height)) {
|
|
974
|
+
rb_str_cat2(result, "/");
|
|
975
|
+
rb_str_append(result, line_height);
|
|
976
|
+
}
|
|
864
977
|
}
|
|
865
978
|
|
|
866
979
|
// family is required
|
|
@@ -1363,6 +1363,14 @@ static color_parser_fn detect_color_format(VALUE value) {
|
|
|
1363
1363
|
return parse_lab;
|
|
1364
1364
|
}
|
|
1365
1365
|
|
|
1366
|
+
// Skip function calls (url, gradient functions, etc.) - they can't be colors
|
|
1367
|
+
// Check if there's a '(' in the string, indicating it's a function
|
|
1368
|
+
const char *paren = strchr(p, '(');
|
|
1369
|
+
if (paren != NULL && (paren - p) < remaining) {
|
|
1370
|
+
// It's a function call like url(...) or linear-gradient(...), not a color
|
|
1371
|
+
return NULL;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1366
1374
|
// Check for named colors (fallback - alphabetic characters)
|
|
1367
1375
|
// Named colors must start with a letter
|
|
1368
1376
|
if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')) {
|
|
@@ -1473,26 +1481,47 @@ struct expand_convert_context {
|
|
|
1473
1481
|
VALUE important;
|
|
1474
1482
|
};
|
|
1475
1483
|
|
|
1484
|
+
// Properties that can contain color values
|
|
1485
|
+
static int is_color_property(const char *prop_name) {
|
|
1486
|
+
// Only these properties can contain actual color values
|
|
1487
|
+
return (strcmp(prop_name, "color") == 0 ||
|
|
1488
|
+
strcmp(prop_name, "background-color") == 0 ||
|
|
1489
|
+
strcmp(prop_name, "border-color") == 0 ||
|
|
1490
|
+
strcmp(prop_name, "border-top-color") == 0 ||
|
|
1491
|
+
strcmp(prop_name, "border-right-color") == 0 ||
|
|
1492
|
+
strcmp(prop_name, "border-bottom-color") == 0 ||
|
|
1493
|
+
strcmp(prop_name, "border-left-color") == 0 ||
|
|
1494
|
+
strcmp(prop_name, "outline-color") == 0 ||
|
|
1495
|
+
strcmp(prop_name, "text-decoration-color") == 0 ||
|
|
1496
|
+
strcmp(prop_name, "column-rule-color") == 0 ||
|
|
1497
|
+
strcmp(prop_name, "caret-color") == 0);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1476
1500
|
// Callback for iterating expanded properties, converting colors, and creating declaration structs
|
|
1477
1501
|
static int convert_expanded_property_callback(VALUE prop_name, VALUE prop_value, VALUE arg) {
|
|
1478
1502
|
struct expand_convert_context *ctx = (struct expand_convert_context *)arg;
|
|
1479
1503
|
|
|
1480
1504
|
if (!NIL_P(prop_value) && TYPE(prop_value) == T_STRING) {
|
|
1481
|
-
|
|
1505
|
+
const char *prop_name_str = RSTRING_PTR(prop_name);
|
|
1482
1506
|
|
|
1483
|
-
//
|
|
1484
|
-
if (
|
|
1485
|
-
parser
|
|
1486
|
-
} else if (matches_color_format(prop_value, ctx->from_format)) {
|
|
1487
|
-
parser = ctx->parser;
|
|
1488
|
-
} else {
|
|
1489
|
-
parser = NULL;
|
|
1490
|
-
}
|
|
1507
|
+
// Only try to convert colors in properties that can contain colors
|
|
1508
|
+
if (is_color_property(prop_name_str)) {
|
|
1509
|
+
color_parser_fn parser;
|
|
1491
1510
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1511
|
+
// Auto-detect or use specified format
|
|
1512
|
+
if (ctx->parser == NULL) {
|
|
1513
|
+
parser = detect_color_format(prop_value);
|
|
1514
|
+
} else if (matches_color_format(prop_value, ctx->from_format)) {
|
|
1515
|
+
parser = ctx->parser;
|
|
1516
|
+
} else {
|
|
1517
|
+
parser = NULL;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
if (parser != NULL) {
|
|
1521
|
+
// Parse → IR → Format
|
|
1522
|
+
struct color_ir color = parser(prop_value);
|
|
1523
|
+
prop_value = ctx->formatter(color, ctx->use_modern_syntax);
|
|
1524
|
+
}
|
|
1496
1525
|
}
|
|
1497
1526
|
|
|
1498
1527
|
VALUE new_decl = rb_struct_new(cDeclaration, prop_name, prop_value, ctx->important, NULL);
|
|
@@ -1617,7 +1646,23 @@ VALUE rb_stylesheet_convert_colors(int argc, VALUE *argv, VALUE self) {
|
|
|
1617
1646
|
continue;
|
|
1618
1647
|
}
|
|
1619
1648
|
|
|
1620
|
-
//
|
|
1649
|
+
// First, try to convert as a value with potentially multiple colors WITHOUT expanding
|
|
1650
|
+
// (e.g., "border-color: #fff #000 #ccc" or "box-shadow: 0 0 10px #ff0000")
|
|
1651
|
+
// This preserves shorthands like "background: linear-gradient(...)" which have no colors
|
|
1652
|
+
VALUE converted_multi = convert_value_with_colors(value, parser, formatter, use_modern_syntax);
|
|
1653
|
+
|
|
1654
|
+
if (!NIL_P(converted_multi)) {
|
|
1655
|
+
DEBUG_PRINTF("Creating new decl with property='%s' value='%s'\n",
|
|
1656
|
+
StringValueCStr(property), StringValueCStr(converted_multi));
|
|
1657
|
+
// Successfully converted multi-value property - keep as shorthand
|
|
1658
|
+
VALUE new_decl = rb_struct_new(cDeclaration, property, converted_multi, important, NULL);
|
|
1659
|
+
rb_ary_push(new_declarations, new_decl);
|
|
1660
|
+
DEBUG_PRINTF("Pushed new_decl to new_declarations\n");
|
|
1661
|
+
continue;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
// No colors found in shorthand value - check if this property needs expansion
|
|
1665
|
+
// (e.g., background shorthand that couldn't be converted as-is)
|
|
1621
1666
|
VALUE expanded = expand_property_if_needed(property, value);
|
|
1622
1667
|
|
|
1623
1668
|
if (!NIL_P(expanded) && TYPE(expanded) == T_HASH && RHASH_SIZE(expanded) > 0) {
|
|
@@ -1635,20 +1680,6 @@ VALUE rb_stylesheet_convert_colors(int argc, VALUE *argv, VALUE self) {
|
|
|
1635
1680
|
}
|
|
1636
1681
|
// If expansion returned empty hash, keep original declaration (e.g., gradients)
|
|
1637
1682
|
|
|
1638
|
-
// Try to convert as a value with potentially multiple colors
|
|
1639
|
-
// (e.g., "border-color: #fff #000 #ccc" or "box-shadow: 0 0 10px #ff0000")
|
|
1640
|
-
VALUE converted_multi = convert_value_with_colors(value, parser, formatter, use_modern_syntax);
|
|
1641
|
-
|
|
1642
|
-
if (!NIL_P(converted_multi)) {
|
|
1643
|
-
DEBUG_PRINTF("Creating new decl with property='%s' value='%s'\n",
|
|
1644
|
-
StringValueCStr(property), StringValueCStr(converted_multi));
|
|
1645
|
-
// Successfully converted multi-value property
|
|
1646
|
-
VALUE new_decl = rb_struct_new(cDeclaration, property, converted_multi, important, NULL);
|
|
1647
|
-
rb_ary_push(new_declarations, new_decl);
|
|
1648
|
-
DEBUG_PRINTF("Pushed new_decl to new_declarations\n");
|
|
1649
|
-
continue;
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
1683
|
// Try single-value color conversion
|
|
1653
1684
|
color_parser_fn single_parser;
|
|
1654
1685
|
|
|
@@ -241,6 +241,15 @@ struct color_ir parse_named(VALUE named_value) {
|
|
|
241
241
|
name_len--;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
// Special case: "transparent" is rgba(0,0,0,0) per CSS spec
|
|
245
|
+
if (name_len == 11 && strncasecmp(name, "transparent", 11) == 0) {
|
|
246
|
+
color.red = 0;
|
|
247
|
+
color.green = 0;
|
|
248
|
+
color.blue = 0;
|
|
249
|
+
color.alpha = 0.0; // Fully transparent
|
|
250
|
+
return color;
|
|
251
|
+
}
|
|
252
|
+
|
|
244
253
|
// Look up the color
|
|
245
254
|
int hex = lookup_named_color(name, name_len);
|
|
246
255
|
|
|
@@ -254,6 +263,7 @@ struct color_ir parse_named(VALUE named_value) {
|
|
|
254
263
|
color.red = (hex >> 16) & 0xFF;
|
|
255
264
|
color.green = (hex >> 8) & 0xFF;
|
|
256
265
|
color.blue = hex & 0xFF;
|
|
266
|
+
// alpha stays at -1.0 (no alpha / opaque)
|
|
257
267
|
|
|
258
268
|
return color;
|
|
259
269
|
}
|
|
@@ -198,8 +198,7 @@ module Cataract
|
|
|
198
198
|
|
|
199
199
|
# Convert declarations to CSS string.
|
|
200
200
|
#
|
|
201
|
-
# Implemented in C for performance
|
|
202
|
-
# The C implementation is defined via rb_define_method and overrides this stub.
|
|
201
|
+
# Implemented in C for performance.
|
|
203
202
|
#
|
|
204
203
|
# @return [String] CSS declaration block string
|
|
205
204
|
#
|
data/lib/cataract/version.rb
CHANGED
data/lib/cataract.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'cataract/version'
|
|
4
|
-
require_relative 'cataract/
|
|
5
|
-
require_relative 'cataract/rule'
|
|
6
|
-
require_relative 'cataract/at_rule'
|
|
4
|
+
require_relative 'cataract/native_extension'
|
|
5
|
+
require_relative 'cataract/rule'
|
|
6
|
+
require_relative 'cataract/at_rule'
|
|
7
7
|
require_relative 'cataract/stylesheet_scope'
|
|
8
8
|
require_relative 'cataract/stylesheet'
|
|
9
9
|
require_relative 'cataract/declarations'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cataract
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James Cook
|
|
@@ -22,6 +22,7 @@ files:
|
|
|
22
22
|
- ".clang-tidy"
|
|
23
23
|
- ".github/workflows/ci-macos.yml"
|
|
24
24
|
- ".github/workflows/ci.yml"
|
|
25
|
+
- ".github/workflows/docs.yml"
|
|
25
26
|
- ".github/workflows/test.yml"
|
|
26
27
|
- ".gitignore"
|
|
27
28
|
- ".overcommit.yml"
|
|
@@ -56,6 +57,7 @@ files:
|
|
|
56
57
|
- benchmarks/yjit_tests.rb
|
|
57
58
|
- cataract.gemspec
|
|
58
59
|
- cliff.toml
|
|
60
|
+
- docs/files/EXAMPLE.md
|
|
59
61
|
- examples/color_conversion_visual_test/color_conversion_test.html
|
|
60
62
|
- examples/color_conversion_visual_test/generate.rb
|
|
61
63
|
- examples/color_conversion_visual_test/template.html.erb
|