@lokascript/compilation-service 3.1.1 → 3.2.0

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.
@@ -1964,6 +1964,39 @@ function makeTypeCheckHandler(negated) {
1964
1964
  };
1965
1965
  };
1966
1966
  }
1967
+ var OF_OPERAND_BP = 70;
1968
+ function parseOfOperand(ctx, minBp = OF_OPERAND_BP) {
1969
+ let right = ctx.parseExpr(minBp);
1970
+ if (ctx.peek()?.value === "in") {
1971
+ const inToken = ctx.advance();
1972
+ const scope = ctx.parseExpr(minBp);
1973
+ right = {
1974
+ type: "binaryExpression",
1975
+ operator: "in",
1976
+ left: right,
1977
+ right: scope,
1978
+ start: right.start,
1979
+ end: scope.end ?? inToken.end,
1980
+ line: right.line ?? inToken.line,
1981
+ column: right.column ?? inToken.column
1982
+ };
1983
+ }
1984
+ return right;
1985
+ }
1986
+ var THE_OF_TARGET_BP = OF_OPERAND_BP + 1;
1987
+ function ofHandler(left, token, ctx) {
1988
+ const right = parseOfOperand(ctx);
1989
+ return {
1990
+ type: "binaryExpression",
1991
+ operator: token.value,
1992
+ left,
1993
+ right,
1994
+ start: left.start,
1995
+ end: right.end ?? token.end,
1996
+ line: left.line ?? token.line,
1997
+ column: left.column ?? token.column
1998
+ };
1999
+ }
1967
2000
  var PARSER_COMPARISON_FRAGMENT = /* @__PURE__ */ new Map([
1968
2001
  ["===", leftAssoc(30)],
1969
2002
  ["!==", leftAssoc(30)],
@@ -2008,7 +2041,7 @@ var PARSER_COMPARISON_FRAGMENT = /* @__PURE__ */ new Map([
2008
2041
  ["is greater than or equal to", leftAssoc(30)],
2009
2042
  ["is less than or equal to", leftAssoc(30)],
2010
2043
  ["in", leftAssoc(30)],
2011
- ["of", rightAssoc(30)],
2044
+ ["of", { infix: { bp: [90, OF_OPERAND_BP], handler: ofHandler } }],
2012
2045
  ["really", leftAssoc(30)],
2013
2046
  [
2014
2047
  "exists",
@@ -2694,6 +2727,48 @@ function parseMaybeNamedArgument(ctx) {
2694
2727
  const value = ctx.parseExpression();
2695
2728
  return name !== void 0 ? { name, value } : { value };
2696
2729
  }
2730
+ function isNakedNamedArgStart(ctx) {
2731
+ if (ctx.check("{"))
2732
+ return false;
2733
+ if (!ctx.checkIdentifierLike())
2734
+ return false;
2735
+ const next = ctx.peekAt(1);
2736
+ return next !== null && next.value === ":";
2737
+ }
2738
+ function parseNakedNamedArgs(ctx, parseValue, command2) {
2739
+ const properties = [];
2740
+ const startPos = ctx.getPosition();
2741
+ do {
2742
+ if (!ctx.checkIdentifierLike())
2743
+ break;
2744
+ const keyToken = ctx.advance();
2745
+ const key = {
2746
+ type: "identifier",
2747
+ name: keyToken.value,
2748
+ start: keyToken.start,
2749
+ end: keyToken.end,
2750
+ line: keyToken.line,
2751
+ column: keyToken.column
2752
+ };
2753
+ ctx.consume(":", `Expected ':' after property name in ${command2} named arguments`);
2754
+ const value = parseValue();
2755
+ if (value) {
2756
+ properties.push({ key, value });
2757
+ }
2758
+ } while (ctx.match(",") && !ctx.isAtEnd());
2759
+ const endPos = ctx.getPosition();
2760
+ return {
2761
+ type: "objectLiteral",
2762
+ properties,
2763
+ start: startPos.start,
2764
+ end: endPos.end,
2765
+ line: startPos.line,
2766
+ column: startPos.column
2767
+ };
2768
+ }
2769
+ function isGluedBeepBang(beep, bang) {
2770
+ return beep?.value === "beep" && bang?.value === "!" && bang.start === beep.end;
2771
+ }
2697
2772
  var CommandNodeBuilder = class _CommandNodeBuilder {
2698
2773
  constructor(name) {
2699
2774
  this.args = [];
@@ -2890,6 +2965,8 @@ function parseTriggerCommand(ctx, identifierNode) {
2890
2965
  const modifiers = {};
2891
2966
  while (!isCommandBoundary(ctx)) {
2892
2967
  if (ctx.check(KEYWORDS.ON) || ctx.check(KEYWORDS.TO)) {
2968
+ if (modifiers["on"])
2969
+ break;
2893
2970
  ctx.advance();
2894
2971
  const target = parseOneArgument(ctx, [KEYWORDS.WITH]);
2895
2972
  if (target)
@@ -3021,6 +3098,8 @@ function parseRepeatCommand(ctx, commandToken) {
3021
3098
  } else if (ctx.check(KEYWORDS.FOREVER)) {
3022
3099
  ctx.advance();
3023
3100
  loopType = KEYWORDS.FOREVER;
3101
+ } else if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
3102
+ loopType = KEYWORDS.FOREVER;
3024
3103
  } else {
3025
3104
  times = ctx.parseExpression();
3026
3105
  if (ctx.check(KEYWORDS.TIMES)) {
@@ -3060,10 +3139,11 @@ function parseRepeatCommand(ctx, commandToken) {
3060
3139
  loopType = result.terminator;
3061
3140
  ctx.advance();
3062
3141
  condition = ctx.parseExpression();
3063
- if (!ctx.check("end")) {
3142
+ if (ctx.check("end")) {
3143
+ ctx.advance();
3144
+ } else if (!ctx.atEndOfInput()) {
3064
3145
  throw new Error('Expected "end" to close repeat block');
3065
3146
  }
3066
- ctx.advance();
3067
3147
  }
3068
3148
  } else {
3069
3149
  const result = ctx.parseCommandListUntilEndOrElse();
@@ -3292,7 +3372,7 @@ function parseIfCommand(ctx, commandToken) {
3292
3372
  }));
3293
3373
  }
3294
3374
  }
3295
- if (!consumedElseIf && !ctx.isAtEnd()) {
3375
+ if (!consumedElseIf && !ctx.atEndOfInput()) {
3296
3376
  ctx.consume(KEYWORDS.END, "Expected 'end' after if block");
3297
3377
  }
3298
3378
  } else {
@@ -3312,7 +3392,6 @@ function parseIfCommand(ctx, commandToken) {
3312
3392
  return CommandNodeBuilder.from(commandToken).withArgs(...args).endingAt(ctx.getPosition()).build();
3313
3393
  }
3314
3394
  function parseForCommand(ctx, commandToken) {
3315
- const args = [];
3316
3395
  let variable = null;
3317
3396
  let collection = null;
3318
3397
  if (ctx.check(KEYWORDS.EACH)) {
@@ -3352,30 +3431,20 @@ function parseForCommand(ctx, commandToken) {
3352
3431
  }
3353
3432
  }
3354
3433
  const commands = ctx.parseCommandListUntilEnd("for");
3355
- args.push({
3356
- type: "identifier",
3357
- name: "for",
3358
- start: commandToken.start,
3359
- end: commandToken.end,
3360
- line: commandToken.line,
3361
- column: commandToken.column
3362
- });
3363
- const forPos = {
3434
+ const pos2 = {
3364
3435
  start: commandToken.start,
3365
3436
  end: commandToken.end,
3366
3437
  line: commandToken.line,
3367
3438
  column: commandToken.column
3368
3439
  };
3369
- args.push(createStringLiteral(variable, forPos));
3370
- args.push(collection);
3371
- if (indexVariable) {
3372
- args.push(createStringLiteral(indexVariable, forPos));
3373
- }
3374
- args.push(createBlock(commands, { ...forPos, end: forPos.end || 0 }));
3375
- return CommandNodeBuilder.from({
3376
- ...commandToken,
3377
- value: "repeat"
3378
- }).withArgs(...args).endingAt(ctx.getPosition()).build();
3440
+ const modifiers = {
3441
+ loopType: toLegacyExpression(createStringLiteral(KEYWORDS.FOR, pos2)),
3442
+ for: toLegacyExpression(createStringLiteral(variable, pos2)),
3443
+ in: collection
3444
+ };
3445
+ if (indexVariable)
3446
+ modifiers["index"] = toLegacyExpression(createStringLiteral(indexVariable, pos2));
3447
+ return CommandNodeBuilder.from({ ...commandToken, value: "repeat" }).withArgs(createBlock(commands, { ...pos2, end: pos2.end || 0 })).withModifiers(modifiers).endingAt(ctx.getPosition()).build();
3379
3448
  }
3380
3449
  function parseMeasureCommand(ctx, identifierNode) {
3381
3450
  const args = [];
@@ -3424,75 +3493,144 @@ function parseMeasureCommand(ctx, identifierNode) {
3424
3493
  }
3425
3494
  return builder.build();
3426
3495
  }
3427
- var CONTEXT_POSSESSIVES = /* @__PURE__ */ new Set(["my", "its", "your"]);
3496
+ var CONTEXT_REFS = /* @__PURE__ */ new Set(["me", "it", "you", "result"]);
3428
3497
  function parseTransitionCommand(ctx, commandToken) {
3429
- const args = [];
3430
- const modifiers = {};
3431
- let property = null;
3432
- let target = null;
3433
- const leadToken = ctx.peek();
3434
- const startsTarget = ctx.checkAnySelector() || ctx.checkContextVar() || ctx.check("<") || isIdentifierLike(leadToken) && CONTEXT_POSSESSIVES.has(leadToken.value);
3435
- if (startsTarget) {
3436
- const parsed = ctx.parsePrimary();
3437
- if ((parsed.type === "possessiveExpression" || parsed.type === "memberExpression" && !parsed.computed) && parsed.object && parsed.property?.name) {
3438
- target = parsed.object;
3439
- property = {
3440
- type: "string",
3441
- value: parsed.property.name,
3442
- start: leadToken.start || 0,
3443
- end: ctx.getPosition().end,
3444
- line: leadToken.line,
3445
- column: leadToken.column
3446
- };
3447
- } else {
3448
- target = parsed;
3449
- if (ctx.check("'s"))
3450
- ctx.advance();
3451
- }
3452
- }
3453
- const firstToken = ctx.peek();
3454
- if (!property && (isIdentifierLike(firstToken) || firstToken.value === "*")) {
3455
- let propertyValue = "";
3456
- if (ctx.check("*")) {
3457
- propertyValue = "*";
3498
+ const pairs = [];
3499
+ do {
3500
+ pairs.push(parseTransitionPair(ctx));
3501
+ } while (startsAnotherPair(ctx));
3502
+ const [first, ...rest] = pairs;
3503
+ const args = first.owner ? [first.owner, first.property] : [first.property];
3504
+ const modifiers = { to: first.to };
3505
+ if (first.from)
3506
+ modifiers["from"] = first.from;
3507
+ if (rest.length > 0)
3508
+ modifiers["pairs"] = pairsNode(rest);
3509
+ for (let i = 0; i < 3 && !ctx.isAtEnd(); i++) {
3510
+ if (ctx.check("over") && !modifiers["over"]) {
3458
3511
  ctx.advance();
3459
- }
3460
- const hyphenatedName = parseHyphenatedName(ctx);
3461
- if (hyphenatedName) {
3462
- propertyValue += hyphenatedName;
3463
- property = {
3464
- type: "string",
3465
- value: propertyValue,
3466
- start: firstToken.start || 0,
3467
- end: ctx.getPosition().end,
3468
- line: firstToken.line,
3469
- column: firstToken.column
3470
- };
3512
+ modifiers["over"] = ctx.parseExpression();
3513
+ } else if (ctx.check("using") && !modifiers["using"]) {
3514
+ ctx.advance();
3515
+ modifiers["using"] = ctx.parseExpression();
3516
+ } else if (ctx.check(KEYWORDS.WITH) && !modifiers["with"]) {
3517
+ ctx.advance();
3518
+ modifiers["with"] = ctx.parsePrimary();
3519
+ } else {
3520
+ break;
3471
3521
  }
3472
3522
  }
3473
- if (!property) {
3474
- throw new Error("Transition command requires a CSS property");
3523
+ return CommandNodeBuilder.from(commandToken).withArgs(...args).withModifiers(modifiers).endingAt(ctx.getPosition()).build();
3524
+ }
3525
+ function parseTransitionPair(ctx) {
3526
+ const { owner, property } = parseTransitionProperty(ctx);
3527
+ let from = null;
3528
+ if (ctx.check("from")) {
3529
+ ctx.advance();
3530
+ from = ctx.parseExpression();
3475
3531
  }
3476
- if (target)
3477
- args.push(target);
3478
- args.push(property);
3479
3532
  if (!ctx.check(KEYWORDS.TO)) {
3480
3533
  throw new Error('Expected "to" keyword after property in transition command');
3481
3534
  }
3482
3535
  ctx.advance();
3483
- const value = ctx.parseExpression();
3484
- modifiers["to"] = value;
3485
- if (ctx.check("over")) {
3486
- ctx.advance();
3487
- const duration = ctx.parseExpression();
3488
- modifiers["over"] = duration;
3536
+ const to = ctx.parseExpression();
3537
+ return { owner, property, from, to };
3538
+ }
3539
+ function parseTransitionProperty(ctx) {
3540
+ const lead = ctx.peek();
3541
+ if (!lead || isKeyword(lead, [KEYWORDS.TO, "from"])) {
3542
+ throw new Error("Transition command requires a CSS property");
3489
3543
  }
3490
- if (ctx.check(KEYWORDS.WITH)) {
3544
+ const expr = ctx.parseExpression();
3545
+ const split = splitStyleExpression(expr);
3546
+ if (split)
3547
+ return { owner: split.owner, property: propertyNode(split.name, lead, ctx) };
3548
+ let name = "";
3549
+ if (ctx.check("*")) {
3550
+ name = "*";
3491
3551
  ctx.advance();
3492
- const timingFunction = ctx.parsePrimary();
3493
- modifiers["with"] = timingFunction;
3494
3552
  }
3495
- return CommandNodeBuilder.from(commandToken).withArgs(...args).withModifiers(modifiers).endingAt(ctx.getPosition()).build();
3553
+ const hyphenated = parseHyphenatedName(ctx);
3554
+ if (!hyphenated)
3555
+ throw new Error("Transition command requires a CSS property");
3556
+ return { owner: expr, property: propertyNode(name + hyphenated, lead, ctx) };
3557
+ }
3558
+ function splitStyleExpression(node) {
3559
+ const n = node;
3560
+ switch (n.type) {
3561
+ case "selector":
3562
+ return typeof n.value === "string" && n.value.startsWith("*") ? { owner: null, name: n.value } : null;
3563
+ case "identifier":
3564
+ return typeof n.name === "string" && !CONTEXT_REFS.has(n.name) ? { owner: null, name: n.name } : null;
3565
+ case "possessiveExpression":
3566
+ case "memberExpression":
3567
+ return !n.computed && n.object && typeof n.property?.name === "string" ? { owner: n.object, name: n.property.name } : null;
3568
+ case "binaryExpression": {
3569
+ if (n.operator !== "of" || !n.left || !n.right)
3570
+ return null;
3571
+ const inner = splitStyleExpression(n.left);
3572
+ return inner && !inner.owner ? { owner: n.right, name: inner.name } : null;
3573
+ }
3574
+ case "propertyOfExpression":
3575
+ return n.target && typeof n.property?.name === "string" ? { owner: n.target, name: n.property.name } : null;
3576
+ default:
3577
+ return null;
3578
+ }
3579
+ }
3580
+ function propertyNode(name, lead, ctx) {
3581
+ return {
3582
+ type: "string",
3583
+ value: name,
3584
+ start: lead.start || 0,
3585
+ end: ctx.getPosition().end,
3586
+ line: lead.line,
3587
+ column: lead.column
3588
+ };
3589
+ }
3590
+ function startsAnotherPair(ctx) {
3591
+ if (ctx.isAtEnd() || isComment(ctx.peek()))
3592
+ return false;
3593
+ if (isKeyword(ctx.peek(), ["over", "using", KEYWORDS.WITH]))
3594
+ return false;
3595
+ return !isCommandBoundary(ctx, ["catch", "finally", "on"]);
3596
+ }
3597
+ function pairsNode(pairs) {
3598
+ const field = (name, value) => ({
3599
+ key: {
3600
+ type: "identifier",
3601
+ name,
3602
+ start: value.start,
3603
+ end: value.end,
3604
+ line: value.line,
3605
+ column: value.column
3606
+ },
3607
+ value
3608
+ });
3609
+ const elements = pairs.map((pair) => {
3610
+ const properties = [field("property", pair.property), field("to", pair.to)];
3611
+ if (pair.owner)
3612
+ properties.push(field("owner", pair.owner));
3613
+ if (pair.from)
3614
+ properties.push(field("from", pair.from));
3615
+ return {
3616
+ type: "objectLiteral",
3617
+ properties,
3618
+ start: (pair.owner ?? pair.property).start,
3619
+ end: pair.to.end,
3620
+ line: (pair.owner ?? pair.property).line,
3621
+ column: (pair.owner ?? pair.property).column
3622
+ };
3623
+ });
3624
+ const firstEl = elements[0];
3625
+ const lastEl = elements[elements.length - 1];
3626
+ return {
3627
+ type: "arrayLiteral",
3628
+ elements,
3629
+ start: firstEl.start,
3630
+ end: lastEl.end,
3631
+ line: firstEl.line,
3632
+ column: firstEl.column
3633
+ };
3496
3634
  }
3497
3635
  function parseStartCommand(ctx, identifierNode) {
3498
3636
  if (!ctx.match("view")) {
@@ -3754,11 +3892,15 @@ function parseSwapCommand(ctx, identifierNode) {
3754
3892
  const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
3755
3893
  if (targetExpr)
3756
3894
  args.push(targetExpr);
3757
- if (!isDelete && consumeOptionalKeyword(ctx, KEYWORDS.WITH)) {
3895
+ const isMorph = identifierNode.name === "morph";
3896
+ if (!isDelete && (consumeOptionalKeyword(ctx, KEYWORDS.WITH) || isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO))) {
3758
3897
  const contentExpr = parseOneArgument(ctx, ["using"]);
3759
3898
  if (contentExpr)
3760
3899
  modifiers["with"] = contentExpr;
3761
3900
  }
3901
+ if (isMorph && modifiers["with"] === void 0) {
3902
+ ctx.addError("Expected 'to' and the content to morph into");
3903
+ }
3762
3904
  return CommandNodeBuilder.fromIdentifier(identifierNode).withArgs(...args).withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) }).endingAt(ctx.getPosition()).build();
3763
3905
  }
3764
3906
  function parseShowHideCommand(ctx, identifierNode) {
@@ -4448,6 +4590,8 @@ function atArgumentBoundary(ctx, grammar) {
4448
4590
  const next = ctx.peek();
4449
4591
  if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === "(")
4450
4592
  return false;
4593
+ if (isGluedBeepBang(next, ctx.peekAt(1)))
4594
+ return false;
4451
4595
  if (isCommandBoundary(ctx, ["catch", "finally", "on"]))
4452
4596
  return true;
4453
4597
  return isKeyword(next, [...grammar.markers]);
@@ -4479,10 +4623,11 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
4479
4623
  }
4480
4624
  }
4481
4625
  const commaList = new Set(grammar.commaList ?? []);
4626
+ const namedArgs = new Set(grammar.namedArgs ?? []);
4482
4627
  while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
4483
4628
  const keyword = ctx.advance().value;
4484
4629
  const key = ctx.resolveKeyword(keyword).toLowerCase();
4485
- let value = ctx.parseExpression();
4630
+ let value = namedArgs.has(key) && isNakedNamedArgStart(ctx) ? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name) : ctx.parseExpression();
4486
4631
  if (value && commaList.has(key) && ctx.check(",")) {
4487
4632
  const elements = [value];
4488
4633
  while (ctx.match(",")) {
@@ -4645,8 +4790,8 @@ function parseFetchCommand(ctx, commandToken) {
4645
4790
  }
4646
4791
  if (ctx.check("with") && !modifiers["with"]) {
4647
4792
  ctx.advance();
4648
- if (isFetchNakedNamedArgStart(ctx)) {
4649
- modifiers["with"] = parseFetchNakedNamedArgs(ctx);
4793
+ if (isNakedNamedArgStart(ctx)) {
4794
+ modifiers["with"] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), "fetch");
4650
4795
  } else {
4651
4796
  modifiers["with"] = ctx.parsePrimary();
4652
4797
  }
@@ -4678,45 +4823,6 @@ function parseFetchCommand(ctx, commandToken) {
4678
4823
  }
4679
4824
  return builder.build();
4680
4825
  }
4681
- function isFetchNakedNamedArgStart(ctx) {
4682
- if (ctx.check("{"))
4683
- return false;
4684
- if (!ctx.checkIdentifierLike())
4685
- return false;
4686
- const next = ctx.peekAt(1);
4687
- return next !== null && next.value === ":";
4688
- }
4689
- function parseFetchNakedNamedArgs(ctx) {
4690
- const properties = [];
4691
- const startPos = ctx.getPosition();
4692
- do {
4693
- if (!ctx.checkIdentifierLike())
4694
- break;
4695
- const keyToken = ctx.advance();
4696
- const key = {
4697
- type: "identifier",
4698
- name: keyToken.value,
4699
- start: keyToken.start,
4700
- end: keyToken.end,
4701
- line: keyToken.line,
4702
- column: keyToken.column
4703
- };
4704
- ctx.consume(":", "Expected ':' after property name in fetch named arguments");
4705
- const value = ctx.parsePrimary();
4706
- if (value) {
4707
- properties.push({ key, value });
4708
- }
4709
- } while (ctx.match(",") && !ctx.isAtEnd());
4710
- const endPos = ctx.getPosition();
4711
- return {
4712
- type: "objectLiteral",
4713
- properties,
4714
- start: startPos.start,
4715
- end: endPos.end,
4716
- line: startPos.line,
4717
- column: startPos.column
4718
- };
4719
- }
4720
4826
  function findJsEndBoundary(ctx, startPos) {
4721
4827
  const input = ctx.getInputSlice(startPos);
4722
4828
  if (!input) {
@@ -4830,6 +4936,7 @@ function parseTellCommand(ctx, identifierNode) {
4830
4936
  const commands = [];
4831
4937
  while (!ctx.isAtEnd()) {
4832
4938
  if (ctx.checkIsCommand()) {
4939
+ const word = ctx.peek().value;
4833
4940
  try {
4834
4941
  ctx.advance();
4835
4942
  const cmd = ctx.parseCommand();
@@ -4838,7 +4945,8 @@ function parseTellCommand(ctx, identifierNode) {
4838
4945
  } else {
4839
4946
  break;
4840
4947
  }
4841
- } catch {
4948
+ } catch (error) {
4949
+ ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
4842
4950
  break;
4843
4951
  }
4844
4952
  if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
@@ -4959,7 +5067,6 @@ var ONE_EXPR = (syntax) => ({
4959
5067
  markers: [],
4960
5068
  syntax
4961
5069
  });
4962
- var BEEP = ONE_EXPR("beep! [<expression>, \u2026]");
4963
5070
  var COMMAND_GRAMMAR = {
4964
5071
  append: { positional: "expression", markers: ["to"], syntax: "append <value> [to <target>]" },
4965
5072
  prepend: { positional: "expression", markers: ["to"], syntax: "prepend <value> [to <target>]" },
@@ -4970,8 +5077,7 @@ var COMMAND_GRAMMAR = {
4970
5077
  syntax: "make (a|an) <type> [from <args>] [called <name>]"
4971
5078
  },
4972
5079
  throw: { positional: "primary", markers: [], syntax: "throw <error>" },
4973
- "beep!": BEEP,
4974
- beep: BEEP,
5080
+ beep: ONE_EXPR("beep! [<expression>, \u2026]"),
4975
5081
  blur: ONE_EXPR("blur [<target>]"),
4976
5082
  break: NONE,
4977
5083
  breakpoint: NONE,
@@ -4995,7 +5101,8 @@ var COMMAND_GRAMMAR = {
4995
5101
  render: {
4996
5102
  positional: "expression",
4997
5103
  markers: ["with"],
4998
- syntax: "render <template> [with <variables>]"
5104
+ namedArgs: ["with"],
5105
+ syntax: "render <template> [with <name>: <value>, \u2026]"
4999
5106
  },
5000
5107
  reset: ONE_EXPR("reset [<form>]"),
5001
5108
  return: ONE_EXPR("return [<value>]"),
@@ -5334,7 +5441,9 @@ var Parser = class _Parser {
5334
5441
  const firstToken = this.peek();
5335
5442
  const prefixEntry = _Parser.PRATT_TABLE.get(firstToken.value);
5336
5443
  let left;
5337
- if (prefixEntry?.prefix) {
5444
+ if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
5445
+ left = this.parseBeepExpression();
5446
+ } else if (prefixEntry?.prefix) {
5338
5447
  const token = this.advance();
5339
5448
  if (this.isAtEnd()) {
5340
5449
  this.addError(`Expected expression after '${token.value}' operator`);
@@ -5353,6 +5462,9 @@ var Parser = class _Parser {
5353
5462
  continue;
5354
5463
  }
5355
5464
  }
5465
+ if (nextToken.value === "*" && this.isSpacedStyleRef(this.current)) {
5466
+ break;
5467
+ }
5356
5468
  const infixEntry = _Parser.PRATT_TABLE.get(nextToken.value);
5357
5469
  if (!infixEntry?.infix) {
5358
5470
  if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
@@ -5406,6 +5518,35 @@ var Parser = class _Parser {
5406
5518
  this.prattCache.set(cacheKey, { node: left, endPos: this.current });
5407
5519
  return left;
5408
5520
  }
5521
+ parseBeepExpression() {
5522
+ const beepToken = this.advance();
5523
+ this.advance();
5524
+ if (this.isAtEnd()) {
5525
+ this.addError("Expected expression after 'beep!'");
5526
+ return this.createErrorNode();
5527
+ }
5528
+ const operand = this.parseExpressionPratt(_Parser.BEEP_OPERAND_BP);
5529
+ return {
5530
+ type: "unaryExpression",
5531
+ operator: "beep!",
5532
+ operand,
5533
+ argument: operand,
5534
+ prefix: true,
5535
+ start: beepToken.start,
5536
+ end: this.previous().end,
5537
+ line: beepToken.line,
5538
+ column: beepToken.column
5539
+ };
5540
+ }
5541
+ isStyleRefStar(i) {
5542
+ const star = this.tokens[i];
5543
+ const name = this.tokens[i + 1];
5544
+ return star?.value === "*" && !!name && name.start === star.end && isIdentifierLike(name);
5545
+ }
5546
+ isSpacedStyleRef(i) {
5547
+ const prev = this.tokens[i - 1];
5548
+ return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
5549
+ }
5409
5550
  tryParseStringPostfix(left, nextToken) {
5410
5551
  const v2 = nextToken.value;
5411
5552
  let unit = null;
@@ -5575,6 +5716,10 @@ var Parser = class _Parser {
5575
5716
  const isStop = () => this.check("end") || extraStops.some((s) => this.check(s));
5576
5717
  debug.parse("\u{1F504} parseCommandListUntilTerminator: Starting (extraStops:", extraStops.join(","), ")");
5577
5718
  while (!this.isAtEnd() && !isStop()) {
5719
+ if (this.checkComment()) {
5720
+ this.advance();
5721
+ continue;
5722
+ }
5578
5723
  debug.parse("\u{1F4CD} Loop iteration, current token:", this.peek().value, "kind:", this.peek().kind);
5579
5724
  let parsedCommand = false;
5580
5725
  const isCommandToken = this.checkIsCommand();
@@ -5646,7 +5791,7 @@ var Parser = class _Parser {
5646
5791
  if (this.check("end")) {
5647
5792
  debug.parse('\u2705 Found "end", consuming it');
5648
5793
  this.advance();
5649
- } else {
5794
+ } else if (!this.atEndOfInput()) {
5650
5795
  debug.parse('\u274C ERROR: Expected "end" but got:', this.peek().value, "at position:", this.peek().start);
5651
5796
  throw new Error(`Expected "end" to close ${construct} block`);
5652
5797
  }
@@ -5658,7 +5803,7 @@ var Parser = class _Parser {
5658
5803
  if (!hasElse) {
5659
5804
  if (this.check("end")) {
5660
5805
  this.advance();
5661
- } else {
5806
+ } else if (!this.atEndOfInput()) {
5662
5807
  throw new Error('Expected "end" to close repeat block');
5663
5808
  }
5664
5809
  }
@@ -5673,7 +5818,10 @@ var Parser = class _Parser {
5673
5818
  if (terminator === "end") {
5674
5819
  this.advance();
5675
5820
  } else if (terminator === "") {
5676
- throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
5821
+ if (!this.atEndOfInput()) {
5822
+ throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
5823
+ }
5824
+ return { commands, terminator: "end" };
5677
5825
  }
5678
5826
  return { commands, terminator };
5679
5827
  }
@@ -6213,7 +6361,9 @@ var Parser = class _Parser {
6213
6361
  parseTopLevelInitBlock() {
6214
6362
  const pos2 = this.getPosition();
6215
6363
  const initCommands = this.parseCommandBlock(["end"]);
6216
- this.consume("end", "Expected 'end' after init block");
6364
+ if (!this.match("end") && !this.atEndOfInput() && !this.checkFeatureStart()) {
6365
+ this.consume("end", "Expected 'end' after init block");
6366
+ }
6217
6367
  return {
6218
6368
  type: "initBlock",
6219
6369
  commands: initCommands,
@@ -6382,10 +6532,27 @@ var Parser = class _Parser {
6382
6532
  ...sourceExpr.column !== void 0 && { column: sourceExpr.column }
6383
6533
  };
6384
6534
  }
6535
+ isArgumentListOnly(open) {
6536
+ let depth = 0;
6537
+ for (let i = open; i < this.tokens.length; i++) {
6538
+ const token = this.tokens[i];
6539
+ if (token.kind !== "operator")
6540
+ continue;
6541
+ if (token.value === "(" || token.value === "[" || token.value === "{")
6542
+ depth++;
6543
+ else if (token.value === ")" || token.value === "]" || token.value === "}") {
6544
+ if (--depth === 0)
6545
+ return i === open + 1;
6546
+ } else if (token.value === "," && depth === 1)
6547
+ return true;
6548
+ }
6549
+ return false;
6550
+ }
6385
6551
  tryParsePseudoCommand(callExpr, sourceExpr) {
6386
6552
  const methodName = callExpr.callee.name;
6387
6553
  const nextToken = this.peek();
6388
- const hasPseudoCommandPattern = _Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) || isIdentifier(nextToken) && !this.isCommand(nextToken.value) || isContextVar(nextToken);
6554
+ const nextIsArticle = nextToken.value.toLowerCase() === "the";
6555
+ const hasPseudoCommandPattern = nextIsArticle || _Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) || isIdentifier(nextToken) && !this.isCommand(nextToken.value) || isContextVar(nextToken);
6389
6556
  if (!hasPseudoCommandPattern) {
6390
6557
  return {
6391
6558
  node: this.callExprToCommandNode(callExpr, sourceExpr),
@@ -6409,7 +6576,9 @@ var Parser = class _Parser {
6409
6576
  });
6410
6577
  }
6411
6578
  let preposition;
6412
- if (_Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
6579
+ if (nextIsArticle) {
6580
+ this.advance();
6581
+ } else if (_Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
6413
6582
  preposition = this.advance().value.toLowerCase();
6414
6583
  }
6415
6584
  let targetExpr;
@@ -6562,10 +6731,18 @@ var Parser = class _Parser {
6562
6731
  this.consume("]", "Expected ']' after event condition");
6563
6732
  }
6564
6733
  let target;
6734
+ let targetExpression;
6565
6735
  if (this.match("from")) {
6566
- const targetToken = this.advance();
6567
- target = targetToken.value;
6568
- debug.parse(`\u{1F527} parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
6736
+ if (this.check("the"))
6737
+ this.advance();
6738
+ if (_Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
6739
+ const startIndex = this.current;
6740
+ targetExpression = this.parseExpression();
6741
+ target = this.tokens.slice(startIndex, this.current).map((t) => t.value).join(" ");
6742
+ } else {
6743
+ target = this.advance().value;
6744
+ }
6745
+ debug.parse(`\u{1F527} parseEventHandler: Parsed 'from' target: ${target}`);
6569
6746
  this.parseTemporalKeywordModifiers(modifiers);
6570
6747
  }
6571
6748
  let attributeName;
@@ -6610,7 +6787,7 @@ var Parser = class _Parser {
6610
6787
  const nextIsOpenParen = this.tokens[this.current + 1]?.value === "(";
6611
6788
  const commandName = this.peek().value.toLowerCase();
6612
6789
  const isSpecialBodyCommand = commandName === "js" || commandName === "tell";
6613
- if (nextIsOpenParen && !isSpecialBodyCommand) {
6790
+ if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
6614
6791
  let expr;
6615
6792
  const savedError = this.error;
6616
6793
  try {
@@ -6638,7 +6815,7 @@ var Parser = class _Parser {
6638
6815
  this.recordDropped(`Command '${cmdToken}' failed to parse and was discarded`);
6639
6816
  }
6640
6817
  }
6641
- } else if (this.checkIdentifier()) {
6818
+ } else if (this.checkIdentifier() || this.checkEventNamedCall()) {
6642
6819
  const token = this.peek();
6643
6820
  if (this.isCommand(token.value)) {
6644
6821
  const cmd = this.parseCommandWithErrorRecovery();
@@ -6724,6 +6901,7 @@ var Parser = class _Parser {
6724
6901
  ...eventParams.length > 0 && { args: eventParams },
6725
6902
  ...condition && { condition: fromLegacyExpression(condition) },
6726
6903
  ...target && { target },
6904
+ ...targetExpression && { targetExpression: fromLegacyExpression(targetExpression) },
6727
6905
  ...attributeName && { attributeName },
6728
6906
  ...watchTarget && { watchTarget: fromLegacyExpression(watchTarget) },
6729
6907
  ...customEventSource && { customEventSource },
@@ -6960,10 +7138,9 @@ var Parser = class _Parser {
6960
7138
  }
6961
7139
  parseCommandCore() {
6962
7140
  const commandToken = this.previous();
6963
- let commandName = commandToken.value;
7141
+ const commandName = commandToken.value;
6964
7142
  if (commandName === "beep" && this.check("!")) {
6965
7143
  this.advance();
6966
- commandName = "beep!";
6967
7144
  }
6968
7145
  if (commandName === "fetch") {
6969
7146
  return parseFetchCommand(this.getContext(), commandToken);
@@ -7056,6 +7233,9 @@ var Parser = class _Parser {
7056
7233
  args.push(this.parsePrimary());
7057
7234
  }
7058
7235
  const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
7236
+ if (funcName === "closest" && args.length > 0 && this.match("to")) {
7237
+ callNode.closestTo = this.parseExpression();
7238
+ }
7059
7239
  if (funcName === "next" || funcName === "previous") {
7060
7240
  const relativePositional = this.parseRelativePositionalModifiers();
7061
7241
  if (relativePositional) {
@@ -7094,6 +7274,9 @@ var Parser = class _Parser {
7094
7274
  }
7095
7275
  parseTheXofY() {
7096
7276
  const positionalKeywords = ["first", "last", "next", "previous", "random", "closest"];
7277
+ if (this.isStyleRefStar(this.current)) {
7278
+ return this.parsePrimary();
7279
+ }
7097
7280
  if (!this.checkIdentifier()) {
7098
7281
  return this.createIdentifier("the");
7099
7282
  }
@@ -7109,7 +7292,7 @@ var Parser = class _Parser {
7109
7292
  return this.createIdentifier(propertyName);
7110
7293
  }
7111
7294
  this.advance();
7112
- const target = this.parseCall();
7295
+ const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
7113
7296
  return {
7114
7297
  type: "propertyOfExpression",
7115
7298
  property: { type: "identifier", name: propertyName },
@@ -7304,6 +7487,13 @@ var Parser = class _Parser {
7304
7487
  checkLiteral() {
7305
7488
  return this.checkPredicate(isLiteral);
7306
7489
  }
7490
+ checkEventNamedCall() {
7491
+ if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
7492
+ return false;
7493
+ const token = this.peek();
7494
+ const next = this.tokens[this.current + 1];
7495
+ return next?.value === "(" && next.start === token.end;
7496
+ }
7307
7497
  checkIdentifierLike() {
7308
7498
  return this.checkPredicate(isIdentifierLike);
7309
7499
  }
@@ -7412,6 +7602,19 @@ var Parser = class _Parser {
7412
7602
  isAtEnd() {
7413
7603
  return this.current >= this.tokens.length;
7414
7604
  }
7605
+ atEndOfInput() {
7606
+ for (let i = this.current; i < this.tokens.length; i++) {
7607
+ if (!isComment(this.tokens[i]))
7608
+ return false;
7609
+ }
7610
+ return true;
7611
+ }
7612
+ checkFeatureStart() {
7613
+ if (this.isAtEnd())
7614
+ return false;
7615
+ const value = this.peek().value;
7616
+ return value === "init" || value === "on" || value === "def" || getRegisteredFeature(value) !== void 0;
7617
+ }
7415
7618
  peek() {
7416
7619
  if (this.isAtEnd()) {
7417
7620
  return { kind: "unknown", value: "", start: 0, end: 0, line: 1, column: 1 };
@@ -7616,6 +7819,7 @@ var Parser = class _Parser {
7616
7819
  match: this.match.bind(this),
7617
7820
  matchOperator: this.matchOperator.bind(this),
7618
7821
  isAtEnd: this.isAtEnd.bind(this),
7822
+ atEndOfInput: this.atEndOfInput.bind(this),
7619
7823
  createIdentifier: this.createIdentifier.bind(this),
7620
7824
  parseExpression: this.parseExpression.bind(this),
7621
7825
  parsePrimary: this.parsePrimary.bind(this),
@@ -7664,7 +7868,15 @@ Parser.POSTFIX_UNARY_OPERATORS = /* @__PURE__ */ new Set([
7664
7868
  "ignoring case"
7665
7869
  ]);
7666
7870
  Parser.PRATT_TABLE = PARSER_TABLE;
7871
+ Parser.BEEP_OPERAND_BP = 80;
7667
7872
  Parser.PSEUDO_COMMAND_PREPOSITIONS = ["from", "on", "with", "into", "at", "to"];
7873
+ Parser.POSITIONAL_SOURCE_HEADS = /* @__PURE__ */ new Set([
7874
+ "closest",
7875
+ "next",
7876
+ "previous",
7877
+ "first",
7878
+ "last"
7879
+ ]);
7668
7880
  function parse(input, options) {
7669
7881
  const tokens = tokenize(input);
7670
7882
  const parser = new Parser(tokens, options, input);
@@ -7846,6 +8058,84 @@ function isNodeList(value) {
7846
8058
  function isDocumentFragment(value) {
7847
8059
  return value !== null && typeof value === "object" && "nodeType" in value && value.nodeType === 11 && "childNodes" in value;
7848
8060
  }
8061
+ function beepValues(me, values) {
8062
+ const outputs = values.map(describeBeepValue);
8063
+ const shown = outputs.filter((output) => announce(me, output.value));
8064
+ if (shown.length === 0)
8065
+ return outputs;
8066
+ console.group("\u{1F514} beep! Debug Output");
8067
+ for (const output of shown) {
8068
+ console.log(`Value:`, output.value);
8069
+ console.log(`Type:`, output.type);
8070
+ console.log(`Representation:`, output.representation);
8071
+ console.log("---");
8072
+ }
8073
+ console.groupEnd();
8074
+ return outputs;
8075
+ }
8076
+ function announce(me, value) {
8077
+ const target = me;
8078
+ if (typeof CustomEvent !== "function" || typeof target?.dispatchEvent !== "function")
8079
+ return true;
8080
+ return target.dispatchEvent(new CustomEvent("hyperscript:beep", {
8081
+ bubbles: true,
8082
+ cancelable: true,
8083
+ detail: { element: me, value }
8084
+ }));
8085
+ }
8086
+ function describeBeepValue(value) {
8087
+ return { value, type: typeOf(value), representation: representationOf(value) };
8088
+ }
8089
+ function typeOf(value) {
8090
+ if (value === null)
8091
+ return "null";
8092
+ if (value === void 0)
8093
+ return "undefined";
8094
+ if (Array.isArray(value))
8095
+ return "array";
8096
+ if (isHTMLElement$2(value))
8097
+ return "HTMLElement";
8098
+ if (typeof Element !== "undefined" && value instanceof Element)
8099
+ return "Element";
8100
+ if (typeof Node !== "undefined" && value instanceof Node)
8101
+ return "Node";
8102
+ if (value instanceof Error)
8103
+ return "Error";
8104
+ if (value instanceof Date)
8105
+ return "Date";
8106
+ if (value instanceof RegExp)
8107
+ return "RegExp";
8108
+ return typeof value;
8109
+ }
8110
+ function representationOf(value) {
8111
+ if (value === null)
8112
+ return "null";
8113
+ if (value === void 0)
8114
+ return "undefined";
8115
+ if (Array.isArray(value)) {
8116
+ return `Array(${value.length}) [${value.slice(0, 3).map((v2) => representationOf(v2)).join(", ")}${value.length > 3 ? "..." : ""}]`;
8117
+ }
8118
+ if (isHTMLElement$2(value)) {
8119
+ const el = value;
8120
+ const tag = el.tagName.toLowerCase();
8121
+ const id = el.id ? `#${el.id}` : "";
8122
+ const classes = el.className ? `.${el.className.split(" ").join(".")}` : "";
8123
+ return `<${tag}${id}${classes}/>`;
8124
+ }
8125
+ if (value instanceof Error)
8126
+ return `Error: ${value.message}`;
8127
+ if (typeof value === "string")
8128
+ return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
8129
+ if (typeof value === "object") {
8130
+ try {
8131
+ const keys = Object.keys(value);
8132
+ return `Object {${keys.slice(0, 3).join(", ")}${keys.length > 3 ? "..." : ""}}`;
8133
+ } catch {
8134
+ return "[Object]";
8135
+ }
8136
+ }
8137
+ return String(value);
8138
+ }
7849
8139
  function convertToNumber(value) {
7850
8140
  if (value === null || value === void 0) {
7851
8141
  return 0;
@@ -7877,6 +8167,16 @@ function convertToNumber(value) {
7877
8167
  }
7878
8168
  return 0;
7879
8169
  }
8170
+ function counterAmount(value) {
8171
+ return typeof value === "number" ? value : parseFloat(String(value));
8172
+ }
8173
+ function counterValue(value) {
8174
+ if (isHTMLElement$2(value)) {
8175
+ const element = value;
8176
+ return counterValue(element.value || element.textContent);
8177
+ }
8178
+ return value ? counterAmount(value) : 0;
8179
+ }
7880
8180
  function getVariableValue(name, context, preferredScope) {
7881
8181
  if (preferredScope === "element") {
7882
8182
  return getElementVar(context, name);
@@ -7978,32 +8278,36 @@ function setElementPropertyValue(propertyPath, value, context) {
7978
8278
  element[property] = value;
7979
8279
  }
7980
8280
  }
8281
+ function isObjectOwner(value) {
8282
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !isHTMLElement$2(value);
8283
+ }
7981
8284
  function getCurrentNumericValue(target, property, scope, context) {
7982
8285
  if (typeof target === "number") {
7983
8286
  return target;
7984
8287
  }
7985
8288
  if (isHTMLElement$2(target)) {
7986
8289
  const element = target;
8290
+ if (property?.startsWith("@"))
8291
+ return counterValue(element.getAttribute(property.slice(1)));
7987
8292
  if (property) {
7988
8293
  if (property.startsWith("data-") || ["id", "class", "title", "alt", "src", "href"].includes(property)) {
7989
- const value = element.getAttribute(property);
7990
- return convertToNumber(value);
8294
+ return counterValue(element.getAttribute(property));
7991
8295
  } else {
7992
- const value = element[property];
7993
- return convertToNumber(value);
8296
+ return counterValue(element[property]);
7994
8297
  }
7995
8298
  } else {
7996
- const value = element.value || element.textContent;
7997
- return convertToNumber(value);
8299
+ return counterValue(element);
7998
8300
  }
7999
8301
  }
8302
+ if (property && isObjectOwner(target)) {
8303
+ return counterValue(target[property]);
8304
+ }
8000
8305
  if (typeof target === "string") {
8001
8306
  if (scope === "element") {
8002
- return convertToNumber(getVariableValue(target, context, "element"));
8307
+ return counterValue(getVariableValue(target, context, "element"));
8003
8308
  }
8004
8309
  if (scope === "global") {
8005
- const value2 = getVariableValue(target, context, "global");
8006
- return convertToNumber(value2);
8310
+ return counterValue(getVariableValue(target, context, "global"));
8007
8311
  }
8008
8312
  if (target.includes(".")) {
8009
8313
  return getElementPropertyValue(target, context);
@@ -8015,14 +8319,17 @@ function getCurrentNumericValue(target, property, scope, context) {
8015
8319
  } else if (target === "you" && context.you) {
8016
8320
  return convertToNumber(context.you.value || 0);
8017
8321
  }
8018
- const value = getVariableValue(target, context);
8019
- return convertToNumber(value);
8322
+ return counterValue(getVariableValue(target, context));
8020
8323
  }
8021
- return convertToNumber(target);
8324
+ return counterValue(target);
8022
8325
  }
8023
8326
  function setTargetValue(target, property, scope, newValue, context) {
8024
8327
  if (isHTMLElement$2(target)) {
8025
8328
  const element = target;
8329
+ if (property?.startsWith("@")) {
8330
+ element.setAttribute(property.slice(1), String(newValue));
8331
+ return;
8332
+ }
8026
8333
  if (property) {
8027
8334
  if (property.startsWith("data-") || ["id", "class", "title", "alt", "src", "href"].includes(property)) {
8028
8335
  element.setAttribute(property, String(newValue));
@@ -8038,6 +8345,10 @@ function setTargetValue(target, property, scope, newValue, context) {
8038
8345
  }
8039
8346
  return;
8040
8347
  }
8348
+ if (property && isObjectOwner(target)) {
8349
+ target[property] = newValue;
8350
+ return;
8351
+ }
8041
8352
  if (typeof target === "string") {
8042
8353
  if (scope === "element") {
8043
8354
  setVariableValue(target, newValue, context, "element");
@@ -8452,13 +8763,6 @@ function isFunction(value) {
8452
8763
  function isInputElement$1(el) {
8453
8764
  return el !== null && typeof el === "object" && "tagName" in el && el.tagName === "INPUT";
8454
8765
  }
8455
- function isFormElement$1(el) {
8456
- if (el === null || typeof el !== "object" || !("tagName" in el)) {
8457
- return false;
8458
- }
8459
- const tag = el.tagName;
8460
- return tag === "INPUT" || tag === "SELECT" || tag === "TEXTAREA";
8461
- }
8462
8766
  function isHTMLElement$1(el) {
8463
8767
  return el !== null && typeof el === "object" && "nodeType" in el && el.nodeType === 1 && "style" in el;
8464
8768
  }
@@ -8509,7 +8813,7 @@ var SPECIAL_DOM_PROPERTIES = {
8509
8813
  innertext: (el) => el.textContent?.trim(),
8510
8814
  innerHTML: (el) => el.innerHTML,
8511
8815
  outerhtml: (el) => el.outerHTML,
8512
- value: (el) => isFormElement$1(el) ? el.value : void 0,
8816
+ value: (el) => "value" in el ? el.value : void 0,
8513
8817
  checked: (el) => isInputElement$1(el) ? el.checked : void 0,
8514
8818
  disabled: (el) => "disabled" in el ? el.disabled : void 0,
8515
8819
  selected: (el) => isOptionElement$1(el) ? el.selected : void 0,
@@ -9154,14 +9458,15 @@ var closestExpression = {
9154
9458
  if (typeof selector !== "string") {
9155
9459
  throw new Error("closest requires a string selector");
9156
9460
  }
9157
- if (!context.me) {
9461
+ const start = args.length > 1 ? args[1] : context.me;
9462
+ if (!(start instanceof Element)) {
9158
9463
  return null;
9159
9464
  }
9160
- return context.me.closest(selector);
9465
+ return start.closest(selector);
9161
9466
  },
9162
9467
  validate(args) {
9163
- if (args.length !== 1) {
9164
- return "closest requires exactly 1 argument (selector)";
9468
+ if (args.length !== 1 && args.length !== 2) {
9469
+ return "closest requires a selector and, optionally, the element to start from";
9165
9470
  }
9166
9471
  const arg = args[0];
9167
9472
  if (typeof arg === "string")
@@ -10625,7 +10930,7 @@ var isEmptyExpression = {
10625
10930
  else if (value instanceof Node || value instanceof Element)
10626
10931
  result = false;
10627
10932
  else if (isObject(value))
10628
- result = Object.keys(value).length === 0;
10933
+ result = value.length === 0;
10629
10934
  else
10630
10935
  result = false;
10631
10936
  return result;
@@ -11681,11 +11986,12 @@ async function evaluateBinaryExpression(node, context) {
11681
11986
  }
11682
11987
  }
11683
11988
  }
11684
- if (leftNode?.type === "selector" && leftNode.fromQuery && typeof leftNode.value === "string") {
11989
+ if (leftNode?.type === "selector" && typeof leftNode.value === "string" && (leftNode.fromQuery || operator === "in" && leftNode.value.startsWith("."))) {
11685
11990
  const root = await evaluateAST(node.right, context);
11686
11991
  const scope = root && typeof root.querySelectorAll === "function" ? root : null;
11687
11992
  if (scope) {
11688
- return Array.from(scope.querySelectorAll(leftNode.value));
11993
+ const query = leftNode.fromQuery ? leftNode.value : escapeSelectorForQuery(leftNode, leftNode.value);
11994
+ return Array.from(scope.querySelectorAll(query));
11689
11995
  }
11690
11996
  }
11691
11997
  }
@@ -11694,6 +12000,12 @@ async function evaluateBinaryExpression(node, context) {
11694
12000
  return getExpr(context, "styleRef").evaluate(context, leftNode.value.slice(1), el);
11695
12001
  }
11696
12002
  if (operator === "of") {
12003
+ if (leftNode?.type === "attributeAccess") {
12004
+ const target = await evaluateAST(node.right, context);
12005
+ if (target == null)
12006
+ return void 0;
12007
+ return getExpr(context, "possessive").evaluate(context, target, `@${leftNode.attributeName}`);
12008
+ }
11697
12009
  const path = propertyPathOf(node.left);
11698
12010
  if (path) {
11699
12011
  const target = await evaluateAST(node.right, context);
@@ -11701,6 +12013,10 @@ async function evaluateBinaryExpression(node, context) {
11701
12013
  for (const seg of path) {
11702
12014
  if (cur == null)
11703
12015
  return void 0;
12016
+ if (isMappableCollection(cur) && !(seg in cur)) {
12017
+ cur = await getExpr(context, "possessive").evaluate(context, cur, seg);
12018
+ continue;
12019
+ }
11704
12020
  cur = isElement(cur) ? getElementProperty$1(cur, seg) : cur[seg];
11705
12021
  }
11706
12022
  return typeof cur === "function" ? cur.bind(target) : cur;
@@ -11725,26 +12041,21 @@ async function evaluateBinaryExpression(node, context) {
11725
12041
  const R = node.ignoringCase ? applyCI(right) : right;
11726
12042
  switch (operator) {
11727
12043
  case "+": {
11728
- let addLeft = left;
11729
- let addRight = right;
11730
12044
  if (node.coerceNumeric) {
11731
- if (typeof addLeft === "string")
11732
- addLeft = convertToNumber(addLeft);
11733
- if (typeof addRight === "string")
11734
- addRight = convertToNumber(addRight);
12045
+ return counterValue(left) + counterAmount(right);
11735
12046
  }
11736
- if (typeof addLeft === "string" || typeof addRight === "string") {
11737
- return String(addLeft ?? "") + String(addRight ?? "");
12047
+ if (typeof left === "string" || typeof right === "string") {
12048
+ return String(left ?? "") + String(right ?? "");
11738
12049
  }
11739
- if (Array.isArray(addLeft)) {
11740
- return Array.isArray(addRight) ? [...addLeft, ...addRight] : [...addLeft, addRight];
12050
+ if (Array.isArray(left)) {
12051
+ return Array.isArray(right) ? [...left, ...right] : [...left, right];
11741
12052
  }
11742
- return unwrapTypedResult(await getExpr(context, "addition").evaluate(context, {
11743
- left: addLeft,
11744
- right: addRight
11745
- }));
12053
+ return unwrapTypedResult(await getExpr(context, "addition").evaluate(context, { left, right }));
11746
12054
  }
11747
12055
  case "-":
12056
+ if (node.coerceNumeric) {
12057
+ return counterValue(left) - counterAmount(right);
12058
+ }
11748
12059
  return unwrapTypedResult(await getExpr(context, "subtraction").evaluate(context, { left, right }));
11749
12060
  case "*":
11750
12061
  return unwrapTypedResult(await getExpr(context, "multiplication").evaluate(context, { left, right }));
@@ -11989,15 +12300,14 @@ async function evaluateAttributeAccessNode(node, context) {
11989
12300
  return `@${attributeName}`;
11990
12301
  }
11991
12302
  async function evaluatePropertyOfExpressionNode(node, context) {
11992
- const propertyNode = node.property;
11993
- if (propertyNode?.type !== "identifier") {
12303
+ const propertyNode2 = node.property;
12304
+ if (propertyNode2?.type !== "identifier") {
11994
12305
  throw new Error('Property name must be an identifier in "the X of Y" pattern');
11995
12306
  }
11996
- const propertyName = propertyNode.name;
12307
+ const propertyName = propertyNode2.name;
11997
12308
  const target = await evaluateAST(node.target, context);
11998
- if (target == null) {
11999
- throw new Error(`Cannot access property "${propertyName}" of ${target}`);
12000
- }
12309
+ if (target == null)
12310
+ return void 0;
12001
12311
  return getExpr(context, "possessive").evaluate(context, target, propertyName);
12002
12312
  }
12003
12313
  async function evaluateTemplateLiteralNode(node, context) {
@@ -12113,6 +12423,9 @@ async function evaluateUnaryExpression(node, context) {
12113
12423
  case "not":
12114
12424
  case "!":
12115
12425
  return logicalExpressions.not.evaluate(context, value);
12426
+ case "beep!":
12427
+ beepValues(context.me, [value]);
12428
+ return value;
12116
12429
  case "no":
12117
12430
  return logicalExpressions.no.evaluate(context, value);
12118
12431
  case "-":
@@ -12201,6 +12514,10 @@ async function evaluateCallExpression(node, context) {
12201
12514
  const args = await resolveCallArgs(node.arguments, funcName, context);
12202
12515
  switch (funcName) {
12203
12516
  case "closest":
12517
+ if (node.closestTo) {
12518
+ const start = unwrapElement(await evaluateAST(node.closestTo, context)) ?? null;
12519
+ return referencesExpressions.closest.evaluate(context, args[0], start);
12520
+ }
12204
12521
  return referencesExpressions.closest.evaluate(context, ...args);
12205
12522
  case "previous":
12206
12523
  case "next": {
@@ -13156,8 +13473,8 @@ var RuntimeBase = class _RuntimeBase {
13156
13473
  }
13157
13474
  case "initBlock":
13158
13475
  case "block": {
13159
- const block = node;
13160
- const ops = (Array.isArray(block.commands) ? block.commands : []).map((c) => this.compile(c));
13476
+ const block2 = node;
13477
+ const ops = (Array.isArray(block2.commands) ? block2.commands : []).map((c) => this.compile(c));
13161
13478
  const consumesHalt = node.type === "initBlock";
13162
13479
  return async (context) => {
13163
13480
  for (const op of ops) {
@@ -13537,12 +13854,22 @@ var RuntimeBase = class _RuntimeBase {
13537
13854
  debug.runtime(`BEHAVIOR: Finished installing ${behaviorName}`);
13538
13855
  }
13539
13856
  async executeEventHandler(node, context) {
13540
- const { event, events, commands, target, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler } = node;
13857
+ const { event, events, commands, target, targetExpression, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler } = node;
13541
13858
  const eventNames = events && events.length > 0 ? events : [event];
13542
13859
  debug.runtime(`BEHAVIOR: executeEventHandler: event='${event}', target='${target}'`);
13543
13860
  let targets = [];
13544
13861
  let globalTarget = null;
13545
- if (target) {
13862
+ if (targetExpression) {
13863
+ const resolved = await this.execute(targetExpression, context);
13864
+ if (resolved === window)
13865
+ globalTarget = window;
13866
+ else if (resolved === document)
13867
+ globalTarget = document;
13868
+ else if (this.isElement(resolved))
13869
+ targets = [resolved];
13870
+ else if (Array.isArray(resolved))
13871
+ targets = resolved.filter((el) => this.isElement(el));
13872
+ } else if (target) {
13546
13873
  const targetLower = typeof target === "string" ? target.toLowerCase() : "";
13547
13874
  if (targetLower === "window" || targetLower === "the window") {
13548
13875
  globalTarget = window;
@@ -15450,12 +15777,79 @@ function parseAttribute(expression) {
15450
15777
  }
15451
15778
  throw new Error(`Invalid attribute syntax: ${expression}`);
15452
15779
  }
15453
- function parseAttributeName(expression) {
15454
- return parseAttribute(expression).name;
15780
+ function parseAttributeName(expression) {
15781
+ return parseAttribute(expression).name;
15782
+ }
15783
+ function parseAttributeWithValue(expression) {
15784
+ const result = parseAttribute(expression);
15785
+ return { name: result.name, value: result.value ?? "" };
15786
+ }
15787
+ function _parseDurationComponents(value) {
15788
+ const trimmed = value.trim();
15789
+ const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
15790
+ if (!match) {
15791
+ return null;
15792
+ }
15793
+ const number = parseFloat(match[1]);
15794
+ const unit = (match[2] || "ms").toLowerCase();
15795
+ if (unit === "ms" || unit === "millisecond" || unit === "milliseconds") {
15796
+ return { number, milliseconds: Math.floor(number) };
15797
+ } else if (unit === "s" || unit === "sec" || unit === "second" || unit === "seconds") {
15798
+ return { number, milliseconds: Math.floor(number * 1e3) };
15799
+ }
15800
+ return null;
15801
+ }
15802
+ function parseDuration(value, defaultMs = 300) {
15803
+ if (typeof value === "number") {
15804
+ return Math.max(0, Math.floor(value));
15805
+ }
15806
+ if (typeof value === "string") {
15807
+ const result = _parseDurationComponents(value);
15808
+ return result ? result.milliseconds : defaultMs;
15809
+ }
15810
+ return defaultMs;
15811
+ }
15812
+ function parseDurationStrict(value) {
15813
+ if (typeof value === "number") {
15814
+ if (value < 0)
15815
+ throw new Error("Duration must be >= 0");
15816
+ return Math.floor(value);
15817
+ }
15818
+ if (typeof value === "string") {
15819
+ const result = _parseDurationComponents(value);
15820
+ if (!result) {
15821
+ throw new Error(`Invalid duration format: "${value}"`);
15822
+ }
15823
+ return result.milliseconds;
15824
+ }
15825
+ throw new Error(`Invalid duration type: ${typeof value}`);
15826
+ }
15827
+ function parseCSSDurations(durationString) {
15828
+ if (!durationString || durationString === "none") {
15829
+ return [0];
15830
+ }
15831
+ return durationString.split(",").map((duration) => {
15832
+ const value = parseFloat(duration.trim());
15833
+ if (isNaN(value))
15834
+ return 0;
15835
+ if (duration.includes("s") && !duration.includes("ms")) {
15836
+ return value * 1e3;
15837
+ }
15838
+ return value;
15839
+ });
15840
+ }
15841
+ function calculateMaxAnimationTime(durations, delays) {
15842
+ let maxTime = 0;
15843
+ for (let i = 0; i < durations.length; i++) {
15844
+ const duration = durations[i] || 0;
15845
+ const delay = delays[i] || 0;
15846
+ const totalTime = duration + delay;
15847
+ maxTime = Math.max(maxTime, totalTime);
15848
+ }
15849
+ return maxTime;
15455
15850
  }
15456
- function parseAttributeWithValue(expression) {
15457
- const result = parseAttribute(expression);
15458
- return { name: result.name, value: result.value ?? "" };
15851
+ function camelToKebab(str) {
15852
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase();
15459
15853
  }
15460
15854
  function batchApply(targets, operation) {
15461
15855
  for (const element of targets) {
@@ -15519,15 +15913,18 @@ function toggleAttribute(element, name, value) {
15519
15913
  }
15520
15914
  }
15521
15915
  }
15916
+ function toCssPropertyName(property) {
15917
+ return property.startsWith("--") ? property : camelToKebab(property);
15918
+ }
15522
15919
  function batchSetStyles(targets, styles) {
15523
15920
  const entries = Object.entries(styles);
15524
15921
  return batchApplyItems(targets, entries, (element, [property, value]) => {
15525
- element.style.setProperty(property, value);
15922
+ element.style.setProperty(toCssPropertyName(property), value);
15526
15923
  });
15527
15924
  }
15528
15925
  function batchRemoveStyles(targets, properties) {
15529
15926
  return batchApplyItems(targets, properties, (element, property) => {
15530
- element.style.removeProperty(property);
15927
+ element.style.removeProperty(toCssPropertyName(property));
15531
15928
  });
15532
15929
  }
15533
15930
  function parseClasses(classValue) {
@@ -16164,73 +16561,6 @@ var RemoveCommand = (() => {
16164
16561
  return RemoveCommand2 = _classThis;
16165
16562
  })();
16166
16563
  var createRemoveCommand = createFactory(RemoveCommand);
16167
- function _parseDurationComponents(value) {
16168
- const trimmed = value.trim();
16169
- const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
16170
- if (!match) {
16171
- return null;
16172
- }
16173
- const number = parseFloat(match[1]);
16174
- const unit = (match[2] || "ms").toLowerCase();
16175
- if (unit === "ms" || unit === "millisecond" || unit === "milliseconds") {
16176
- return { number, milliseconds: Math.floor(number) };
16177
- } else if (unit === "s" || unit === "sec" || unit === "second" || unit === "seconds") {
16178
- return { number, milliseconds: Math.floor(number * 1e3) };
16179
- }
16180
- return null;
16181
- }
16182
- function parseDuration(value, defaultMs = 300) {
16183
- if (typeof value === "number") {
16184
- return Math.max(0, Math.floor(value));
16185
- }
16186
- if (typeof value === "string") {
16187
- const result = _parseDurationComponents(value);
16188
- return result ? result.milliseconds : defaultMs;
16189
- }
16190
- return defaultMs;
16191
- }
16192
- function parseDurationStrict(value) {
16193
- if (typeof value === "number") {
16194
- if (value < 0)
16195
- throw new Error("Duration must be >= 0");
16196
- return Math.floor(value);
16197
- }
16198
- if (typeof value === "string") {
16199
- const result = _parseDurationComponents(value);
16200
- if (!result) {
16201
- throw new Error(`Invalid duration format: "${value}"`);
16202
- }
16203
- return result.milliseconds;
16204
- }
16205
- throw new Error(`Invalid duration type: ${typeof value}`);
16206
- }
16207
- function parseCSSDurations(durationString) {
16208
- if (!durationString || durationString === "none") {
16209
- return [0];
16210
- }
16211
- return durationString.split(",").map((duration) => {
16212
- const value = parseFloat(duration.trim());
16213
- if (isNaN(value))
16214
- return 0;
16215
- if (duration.includes("s") && !duration.includes("ms")) {
16216
- return value * 1e3;
16217
- }
16218
- return value;
16219
- });
16220
- }
16221
- function calculateMaxAnimationTime(durations, delays) {
16222
- let maxTime = 0;
16223
- for (let i = 0; i < durations.length; i++) {
16224
- const duration = durations[i] || 0;
16225
- const delay = delays[i] || 0;
16226
- const totalTime = duration + delay;
16227
- maxTime = Math.max(maxTime, totalTime);
16228
- }
16229
- return maxTime;
16230
- }
16231
- function camelToKebab(str) {
16232
- return str.replace(/([A-Z])/g, "-$1").toLowerCase();
16233
- }
16234
16564
  function detectSmartElementType(elements) {
16235
16565
  if (elements.length === 0)
16236
16566
  return null;
@@ -16544,13 +16874,13 @@ async function resolveAnyPropertyTarget(node, evaluator, context) {
16544
16874
  const anyNode = node;
16545
16875
  if (anyNode?.type === "possessiveExpression" || anyNode?.type === "memberExpression") {
16546
16876
  const objectNode = anyNode.object;
16547
- const propertyNode = anyNode.property;
16877
+ const propertyNode2 = anyNode.property;
16548
16878
  let element = await evaluator.evaluate(objectNode, context);
16549
16879
  if (Array.isArray(element))
16550
16880
  element = element[0];
16551
16881
  if (!isHTMLElement$2(element))
16552
16882
  return null;
16553
- const propertyName = propertyNode?.name || propertyNode?.value;
16883
+ const propertyName = propertyNode2?.name || propertyNode2?.value;
16554
16884
  if (!propertyName)
16555
16885
  return null;
16556
16886
  return { element, property: propertyName };
@@ -16668,9 +16998,9 @@ var ToggleCommand = (() => {
16668
16998
  if (!raw.args?.length)
16669
16999
  throw new Error("toggle command requires an argument");
16670
17000
  const firstArg = raw.args[0];
16671
- const propertyTarget = await resolveAnyPropertyTarget(firstArg, evaluator, context);
16672
- if (propertyTarget) {
16673
- return { type: "property", target: propertyTarget };
17001
+ const propertyTarget2 = await resolveAnyPropertyTarget(firstArg, evaluator, context);
17002
+ if (propertyTarget2) {
17003
+ return { type: "property", target: propertyTarget2 };
16674
17004
  }
16675
17005
  if (firstArg.type === "selector" && typeof firstArg.value === "string" && firstArg.value.startsWith("*")) {
16676
17006
  let expression2 = firstArg.value;
@@ -16904,13 +17234,13 @@ var PutCommand = (() => {
16904
17234
  let targetSelector = null, memberPath, variableName;
16905
17235
  if (targetArg) {
16906
17236
  const tt = nodeType(targetArg);
16907
- const propertyTarget = await resolveAnyPropertyTarget(targetArg, evaluator, context);
16908
- if (propertyTarget) {
17237
+ const propertyTarget2 = await resolveAnyPropertyTarget(targetArg, evaluator, context);
17238
+ if (propertyTarget2) {
16909
17239
  return {
16910
17240
  value,
16911
- targets: [propertyTarget.element],
17241
+ targets: [propertyTarget2.element],
16912
17242
  position: "into",
16913
- memberPath: propertyTarget.property
17243
+ memberPath: propertyTarget2.property
16914
17244
  };
16915
17245
  }
16916
17246
  if (tt === "memberExpression") {
@@ -17050,7 +17380,11 @@ var PutCommand = (() => {
17050
17380
  }
17051
17381
  if (isInsertableNode(v2))
17052
17382
  return v2;
17053
- return v2 == null ? "" : String(v2);
17383
+ if (v2 == null)
17384
+ return "null";
17385
+ if (Array.isArray(v2))
17386
+ return arrayFragment(v2);
17387
+ return String(v2);
17054
17388
  }
17055
17389
  looksLikeCss(s) {
17056
17390
  if (!s)
@@ -17135,6 +17469,19 @@ var PutCommand = (() => {
17135
17469
  })();
17136
17470
  return PutCommand2 = _classThis;
17137
17471
  })();
17472
+ function arrayFragment(items) {
17473
+ const fragment = document.createDocumentFragment();
17474
+ for (const item of items) {
17475
+ if (isInsertableNode(item)) {
17476
+ fragment.append(item);
17477
+ continue;
17478
+ }
17479
+ const template = document.createElement("template");
17480
+ template.innerHTML = item === null ? "" : String(item);
17481
+ fragment.append(template.content);
17482
+ }
17483
+ return fragment;
17484
+ }
17138
17485
  var createPutCommand = createFactory(PutCommand);
17139
17486
  var HTML_ESCAPE = {
17140
17487
  "&": "&amp;",
@@ -18321,10 +18668,28 @@ var STRATEGY_KEYWORDS = {
18321
18668
  innermorph: "morph",
18322
18669
  outermorph: "morphOuter"
18323
18670
  };
18671
+ function loneRootWithTag(content, tagName) {
18672
+ let root;
18673
+ if (typeof content === "string") {
18674
+ const template = document.createElement("template");
18675
+ template.innerHTML = content;
18676
+ root = template.content.firstElementChild;
18677
+ if (!root || root.nextElementSibling)
18678
+ return null;
18679
+ } else {
18680
+ root = content.cloneNode(true);
18681
+ }
18682
+ return root.tagName === tagName && isHTMLElement$2(root) ? root : null;
18683
+ }
18324
18684
  function executeSwap(target, content, strategy, morphOptions) {
18325
18685
  const contentStr = content !== null && !isHTMLElement$2(content) ? content : "";
18326
18686
  const contentEl = isHTMLElement$2(content) ? content : null;
18327
18687
  switch (strategy) {
18688
+ case "morphMerge": {
18689
+ const root = content !== null ? loneRootWithTag(content, target.tagName) : null;
18690
+ executeSwap(target, root ?? content, root ? "morphOuter" : "morph", morphOptions);
18691
+ break;
18692
+ }
18328
18693
  case "morph":
18329
18694
  if (content !== null) {
18330
18695
  try {
@@ -18594,7 +18959,7 @@ var MorphCommand = (() => {
18594
18959
  if (args.length === 0 && modifiers.with === void 0) {
18595
18960
  throw new Error("[HyperFixi] morph: command requires arguments");
18596
18961
  }
18597
- const strategy = literalText(modifiers.strategy)?.toLowerCase() === "over" ? "morphOuter" : "morph";
18962
+ const strategy = literalText(modifiers.strategy)?.toLowerCase() === "over" ? "morphOuter" : "morphMerge";
18598
18963
  const useViewTransition = modifiers.viewTransition !== void 0;
18599
18964
  const targetNode = modifiers.on !== void 0 ? modifiers.on : args[0];
18600
18965
  const contentNode = modifiers.with !== void 0 ? modifiers.with : modifiers.on !== void 0 ? args[0] : args[1];
@@ -19862,6 +20227,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
19862
20227
  }
19863
20228
  return { kind: "property", target: property };
19864
20229
  }
20230
+ if (options.ofAndObjectProperty) {
20231
+ const access = ofOrPossessiveAccess(n);
20232
+ if (access) {
20233
+ const owner = await evaluator.evaluate(access.owner, context);
20234
+ const element = Array.isArray(owner) ? owner[0] : owner;
20235
+ if (access.isOf && isHTMLElement$2(element)) {
20236
+ return { kind: "property", target: { element, property: access.name } };
20237
+ }
20238
+ if (isObjectOwner(owner)) {
20239
+ return { kind: "member", container: owner, property: access.name };
20240
+ }
20241
+ }
20242
+ }
19865
20243
  if (options.bareReference && (nodeType === "identifier" || nodeType === "variable" || nodeType === "symbol") && typeof n.name === "string") {
19866
20244
  const rawScope = n.scope;
19867
20245
  const scope = rawScope === "element" || rawScope === "global" ? rawScope : void 0;
@@ -19869,6 +20247,13 @@ async function resolveWriteTarget(node, evaluator, context, options) {
19869
20247
  }
19870
20248
  return null;
19871
20249
  }
20250
+ function ofOrPossessiveAccess(n) {
20251
+ const [owner, property, isOf] = n.type === "possessiveExpression" ? [n.object, n.property, false] : n.type === "propertyOfExpression" ? [n.target, n.property, false] : n.type === "binaryExpression" && n.operator === "of" ? [n.right, n.left, true] : [void 0, void 0, false];
20252
+ const name = property?.name;
20253
+ if (!owner || typeof name !== "string" || !/^[A-Za-z_$][\w$]*$/.test(name))
20254
+ return null;
20255
+ return { owner, name, isOf };
20256
+ }
19872
20257
  function toSetInput(target, value) {
19873
20258
  switch (target.kind) {
19874
20259
  case "node-write":
@@ -19889,10 +20274,18 @@ function toSetInput(target, value) {
19889
20274
  property: target.target.property,
19890
20275
  value
19891
20276
  };
20277
+ case "member":
20278
+ return {
20279
+ type: "member-assignment",
20280
+ container: target.container,
20281
+ property: target.property,
20282
+ value
20283
+ };
19892
20284
  default:
19893
20285
  throw new Error(`set: unrequested write-target rung '${target.kind}'`);
19894
20286
  }
19895
20287
  }
20288
+ var SET_RUNGS = { nodeWriters: true, styleSplit: true, ofAndObjectProperty: true };
19896
20289
  var SetCommand = (() => {
19897
20290
  let _classDecorators = [command({ name: "set" })];
19898
20291
  let _classDescriptor;
@@ -19911,8 +20304,7 @@ var SetCommand = (() => {
19911
20304
  const argScope = firstArg?.scope;
19912
20305
  const writeTarget = await resolveWriteTarget(firstArg, evaluator, context, {
19913
20306
  scopeElements: () => this.resolveTargets(raw.modifiers.on, evaluator, context),
19914
- nodeWriters: true,
19915
- styleSplit: true
20307
+ ...SET_RUNGS
19916
20308
  });
19917
20309
  if (writeTarget) {
19918
20310
  return toSetInput(writeTarget, await this.extractValue(raw, evaluator, context));
@@ -20036,13 +20428,13 @@ var SetCommand = (() => {
20036
20428
  }
20037
20429
  async tryParseMemberExpression(firstArg, raw, evaluator, context) {
20038
20430
  const objectNode = firstArg.object;
20039
- const propertyNode = firstArg.property;
20040
- if (objectNode?.name && propertyNode?.name) {
20431
+ const propertyNode2 = firstArg.property;
20432
+ if (objectNode?.name && propertyNode2?.name) {
20041
20433
  const objectName = objectNode.name.toLowerCase();
20042
20434
  if (["me", "my", "it", "its", "you", "your"].includes(objectName)) {
20043
20435
  const element = resolvePossessive(objectName, context);
20044
20436
  const value = await this.extractValue(raw, evaluator, context);
20045
- return { type: "property", element, property: propertyNode.name, value };
20437
+ return { type: "property", element, property: propertyNode2.name, value };
20046
20438
  }
20047
20439
  }
20048
20440
  const objectAst = firstArg.object;
@@ -20058,7 +20450,7 @@ var SetCommand = (() => {
20058
20450
  container = await evaluator.evaluate(objectAst, context);
20059
20451
  }
20060
20452
  if (container != null && typeof container === "object") {
20061
- const property = computed ? String(await evaluator.evaluate(firstArg.property, context)) : propertyNode?.name || "";
20453
+ const property = computed ? String(await evaluator.evaluate(firstArg.property, context)) : propertyNode2?.name || "";
20062
20454
  if (property) {
20063
20455
  const value = await this.extractValue(raw, evaluator, context);
20064
20456
  return {
@@ -20213,12 +20605,24 @@ function getNodeType(node) {
20213
20605
  return "unknown";
20214
20606
  return node.type || "unknown";
20215
20607
  }
20608
+ function propertyTarget(node) {
20609
+ const n = node;
20610
+ const owner = n.type === "propertyOfExpression" ? n.target : n.type === "possessiveExpression" || n.type === "memberExpression" || n.type === "propertyAccess" ? n.object : void 0;
20611
+ if (!owner || n.computed)
20612
+ return null;
20613
+ const p = n.property;
20614
+ const name = typeof p === "string" ? p : typeof p?.name === "string" ? p.name : void 0;
20615
+ if (!name || !/^[A-Za-z_$][\w$-]*$/.test(name))
20616
+ return null;
20617
+ return { owner, name };
20618
+ }
20216
20619
  async function parseNumericTargetInput(raw, evaluator, context, commandName) {
20217
20620
  if (!raw.args || raw.args.length === 0) {
20218
20621
  throw new Error(`${commandName} command requires a target`);
20219
20622
  }
20220
20623
  const targetArg = raw.args[0];
20221
20624
  let target;
20625
+ let property;
20222
20626
  let extractedScope;
20223
20627
  let customWrite;
20224
20628
  const nodeType = getNodeType(targetArg);
@@ -20231,7 +20635,20 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
20231
20635
  target = targetArg.value;
20232
20636
  } else {
20233
20637
  const writer = getRegisteredNodeWriter(nodeType);
20234
- if (writer) {
20638
+ const attribute = writer ? null : await resolveAttributeWriteTarget(targetArg, evaluator, context, async () => context.me ? [context.me] : []);
20639
+ const prop = writer || attribute ? null : propertyTarget(targetArg);
20640
+ const owner = prop ? await evaluator.evaluate(prop.owner, context) : void 0;
20641
+ const ownerElement = Array.isArray(owner) ? owner[0] : owner;
20642
+ if (attribute?.elements[0]) {
20643
+ target = attribute.elements[0];
20644
+ property = `@${attribute.name}`;
20645
+ } else if (prop && isHTMLElement$2(ownerElement)) {
20646
+ target = ownerElement;
20647
+ property = prop.name;
20648
+ } else if (prop && isObjectOwner(owner)) {
20649
+ target = owner;
20650
+ property = prop.name;
20651
+ } else if (writer) {
20235
20652
  const currentRaw = await evaluator.evaluate(targetArg, context);
20236
20653
  const currentValue = typeof currentRaw === "number" ? currentRaw : currentRaw == null ? 0 : Number(currentRaw) || 0;
20237
20654
  target = currentValue;
@@ -20260,24 +20677,20 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
20260
20677
  const evaluated = await evaluator.evaluate(arg, context);
20261
20678
  if (typeof evaluated === "number") {
20262
20679
  amount = evaluated;
20680
+ } else if (evaluated == null) {
20681
+ amount = NaN;
20263
20682
  }
20264
20683
  }
20265
20684
  }
20266
20685
  const byModifier = raw.modifiers?.by;
20267
20686
  if (byModifier) {
20268
- const byValue = byModifier.value;
20269
- if (getNodeType(byModifier) === "literal" && typeof byValue === "number") {
20270
- amount = byValue;
20271
- } else if (getNodeType(byModifier) !== "literal") {
20272
- const evaluated = await evaluator.evaluate(byModifier, context);
20273
- if (typeof evaluated === "number") {
20274
- amount = evaluated;
20275
- }
20276
- }
20687
+ const value = getNodeType(byModifier) === "literal" ? byModifier.value : await evaluator.evaluate(byModifier, context);
20688
+ amount = counterAmount(value);
20277
20689
  }
20278
20690
  return {
20279
20691
  target,
20280
20692
  amount,
20693
+ ...property && { property },
20281
20694
  ...scope && { scope },
20282
20695
  ...customWrite && { customWrite }
20283
20696
  };
@@ -20299,7 +20712,7 @@ var NumericModifyCommand = (() => {
20299
20712
  async execute(input, context) {
20300
20713
  const { target, property, scope, amount = 1, operation = "increment", customWrite } = input;
20301
20714
  if (customWrite) {
20302
- const delta = isFinite(amount) ? amount : 1;
20715
+ const delta = amount;
20303
20716
  const newValue2 = operation === "increment" ? customWrite.currentValue + delta : customWrite.currentValue - delta;
20304
20717
  await customWrite.writer(customWrite.node, newValue2, context);
20305
20718
  Object.assign(context, { it: newValue2 });
@@ -20310,8 +20723,7 @@ var NumericModifyCommand = (() => {
20310
20723
  if (isNaN(currentValue)) {
20311
20724
  newValue = NaN;
20312
20725
  } else {
20313
- const delta = isFinite(amount) ? amount : 1;
20314
- newValue = operation === "increment" ? currentValue + delta : currentValue - delta;
20726
+ newValue = operation === "increment" ? currentValue + amount : currentValue - amount;
20315
20727
  }
20316
20728
  setTargetValue(target, property, scope, newValue, context);
20317
20729
  Object.assign(context, { it: newValue });
@@ -20737,9 +21149,9 @@ var GoCommand = (() => {
20737
21149
  }
20738
21150
  const { target, position, offset, behavior } = input;
20739
21151
  if (typeof window !== "undefined") {
20740
- const block = this.mapVerticalPosition(position.vertical);
21152
+ const block2 = this.mapVerticalPosition(position.vertical);
20741
21153
  const inline = this.mapHorizontalPosition(position.horizontal);
20742
- target.scrollIntoView?.({ behavior, block, inline });
21154
+ target.scrollIntoView?.({ behavior, block: block2, inline });
20743
21155
  if (offset !== 0) {
20744
21156
  const { x, y } = this.calculateScrollPosition(target, position, offset);
20745
21157
  window.scrollTo?.({ left: x, top: y, behavior });
@@ -21374,7 +21786,10 @@ function createUntilLoopConfig(conditionExpr, evaluateCondition2, context, index
21374
21786
  };
21375
21787
  const config2 = {
21376
21788
  type: "until",
21377
- shouldContinue: () => !evaluateCondition2(conditionExpr, context)
21789
+ shouldContinue: () => {
21790
+ const met = evaluateCondition2(conditionExpr, context);
21791
+ return met instanceof Promise ? met.then((value) => !value) : !met;
21792
+ }
21378
21793
  };
21379
21794
  return { config: config2, iterCtx };
21380
21795
  }
@@ -21409,7 +21824,7 @@ function createForeverLoopConfig(indexVariable, maxIterations = 1e4) {
21409
21824
  };
21410
21825
  return { config: config2, iterCtx };
21411
21826
  }
21412
- async function slotText(node, evaluator, context) {
21827
+ async function slotText$1(node, evaluator, context) {
21413
21828
  if (!node)
21414
21829
  return void 0;
21415
21830
  const n = node;
@@ -21422,6 +21837,18 @@ async function slotText(node, evaluator, context) {
21422
21837
  const v2 = await evaluator.evaluate(node, context);
21423
21838
  return typeof v2 === "string" ? v2 : void 0;
21424
21839
  }
21840
+ function isEventTarget(value) {
21841
+ return typeof value?.addEventListener === "function";
21842
+ }
21843
+ async function evaluateLoopCondition(condition, context) {
21844
+ if (typeof condition === "function")
21845
+ return Boolean(await condition());
21846
+ return evaluateCondition(condition, context);
21847
+ }
21848
+ function loopCount(value) {
21849
+ const count = Number(value);
21850
+ return Number.isNaN(count) ? 0 : count;
21851
+ }
21425
21852
  var RepeatCommand = (() => {
21426
21853
  let _classDecorators = [command({ name: "repeat" })];
21427
21854
  let _classDescriptor;
@@ -21458,7 +21885,7 @@ var RepeatCommand = (() => {
21458
21885
  }
21459
21886
  }
21460
21887
  const m = raw.modifiers ?? {};
21461
- const text = (node) => slotText(node, evaluator, context);
21888
+ const text = (node) => slotText$1(node, evaluator, context);
21462
21889
  const loopType = await text(m.loopType) ?? null;
21463
21890
  const bottomTested = m.bottomTested ? await evaluator.evaluate(m.bottomTested, context) : false;
21464
21891
  if (loopType === "for" || m.for) {
@@ -21476,17 +21903,24 @@ var RepeatCommand = (() => {
21476
21903
  };
21477
21904
  }
21478
21905
  if (loopType === "times" || m.times) {
21479
- const countValue = m.times && await evaluator.evaluate(m.times, context);
21480
- const count = typeof countValue === "number" ? countValue : parseInt(String(countValue), 10);
21481
- if (isNaN(count))
21906
+ if (!m.times)
21482
21907
  throw new Error("times loops require a count number");
21908
+ const count = loopCount(await evaluator.evaluate(m.times, context));
21483
21909
  return { type: "times", count, indexVariable, commands, elseCommands };
21484
21910
  }
21485
21911
  if (loopType === "while" || m.while) {
21486
21912
  const condition = m.while;
21487
21913
  if (!condition)
21488
21914
  throw new Error("while loops require a condition");
21489
- return { type: "while", condition, indexVariable, commands, elseCommands, bottomTested };
21915
+ const check = () => evaluator.evaluate(condition, context);
21916
+ return {
21917
+ type: "while",
21918
+ condition: check,
21919
+ indexVariable,
21920
+ commands,
21921
+ elseCommands,
21922
+ bottomTested
21923
+ };
21490
21924
  }
21491
21925
  if (loopType === "until-event" || m.event) {
21492
21926
  const eventName = await text(m.event);
@@ -21502,7 +21936,7 @@ var RepeatCommand = (() => {
21502
21936
  eventTarget = window;
21503
21937
  } else {
21504
21938
  const target = await evaluator.evaluate(m.from, context);
21505
- if (target instanceof EventTarget)
21939
+ if (isEventTarget(target))
21506
21940
  eventTarget = target;
21507
21941
  else if (target === "document")
21508
21942
  eventTarget = document;
@@ -21523,7 +21957,15 @@ var RepeatCommand = (() => {
21523
21957
  const condition = m.until;
21524
21958
  if (!condition)
21525
21959
  throw new Error("until loops require a condition");
21526
- return { type: "until", condition, indexVariable, commands, elseCommands, bottomTested };
21960
+ const check = () => evaluator.evaluate(condition, context);
21961
+ return {
21962
+ type: "until",
21963
+ condition: check,
21964
+ indexVariable,
21965
+ commands,
21966
+ elseCommands,
21967
+ bottomTested
21968
+ };
21527
21969
  }
21528
21970
  if (loopType === "forever") {
21529
21971
  return { type: "forever", indexVariable, commands, elseCommands };
@@ -21541,10 +21983,10 @@ var RepeatCommand = (() => {
21541
21983
  ({ config: config2, iterCtx } = createTimesLoopConfig(count, indexVariable));
21542
21984
  break;
21543
21985
  case "while":
21544
- ({ config: config2, iterCtx } = createWhileLoopConfig(condition, evaluateCondition, context, indexVariable));
21986
+ ({ config: config2, iterCtx } = createWhileLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
21545
21987
  break;
21546
21988
  case "until":
21547
- ({ config: config2, iterCtx } = createUntilLoopConfig(condition, evaluateCondition, context, indexVariable));
21989
+ ({ config: config2, iterCtx } = createUntilLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
21548
21990
  break;
21549
21991
  case "until-event":
21550
21992
  ({ config: config2, iterCtx } = createUntilEventLoopConfig(eventName, eventTarget, indexVariable));
@@ -22526,6 +22968,29 @@ function namedPropertyOf(node) {
22526
22968
  }
22527
22969
  return null;
22528
22970
  }
22971
+ var RESTORE_KEYWORDS = /* @__PURE__ */ new Set(["initial", "inherit", "unset", "revert"]);
22972
+ function elementsOf(value) {
22973
+ if (Array.isArray(value) || isNodeList(value)) {
22974
+ return Array.from(value).filter(isHTMLElement$2);
22975
+ }
22976
+ return isHTMLElement$2(value) ? [value] : [];
22977
+ }
22978
+ async function valueOf(node, evaluator, context) {
22979
+ const value = await evaluator.evaluate(node, context);
22980
+ const name = node.name;
22981
+ if (value === void 0 && node.type === "identifier" && typeof name === "string")
22982
+ return name;
22983
+ return value;
22984
+ }
22985
+ function longestTransitionMs(css) {
22986
+ let longest;
22987
+ for (const item of css.split(",")) {
22988
+ const times = [...item.matchAll(/(\d*\.?\d+)(ms|s)\b/g)].map((m) => parseFloat(m[1]) * (m[2] === "s" ? 1e3 : 1));
22989
+ if (times.length > 0)
22990
+ longest = Math.max(longest ?? 0, times[0] + (times[1] ?? 0));
22991
+ }
22992
+ return longest;
22993
+ }
22529
22994
  var TransitionCommand = (() => {
22530
22995
  let _classDecorators = [command({ name: "transition" })];
22531
22996
  let _classDescriptor;
@@ -22541,10 +23006,9 @@ var TransitionCommand = (() => {
22541
23006
  let property;
22542
23007
  let target;
22543
23008
  const firstArg = await evaluator.evaluate(raw.args[0], context);
22544
- const asElement = (value2) => Array.isArray(value2) || isNodeList(value2) ? value2[0] : value2;
22545
- const firstAsElement = asElement(firstArg);
22546
- if (isHTMLElement$2(firstAsElement) || typeof firstArg === "string" && /^[#.]|^(?:me|it|you)$/.test(firstArg)) {
22547
- target = firstAsElement;
23009
+ const owners = elementsOf(firstArg);
23010
+ if (owners.length > 0 || typeof firstArg === "string" && /^[#.]|^(?:me|it|you)$/.test(firstArg)) {
23011
+ target = owners.length === 1 ? owners[0] : owners.length > 1 ? owners : firstArg;
22548
23012
  property = String(await evaluator.evaluate(raw.args[1], context));
22549
23013
  } else if (raw.args.length >= 2) {
22550
23014
  throw new Error("transition: target element not found");
@@ -22554,9 +23018,9 @@ var TransitionCommand = (() => {
22554
23018
  throw new Error("transition requires a CSS property");
22555
23019
  property = named.property;
22556
23020
  if (named.objectNode !== void 0) {
22557
- const owner = asElement(await evaluator.evaluate(named.objectNode, context));
22558
- if (isHTMLElement$2(owner))
22559
- target = owner;
23021
+ const owner = elementsOf(await evaluator.evaluate(named.objectNode, context));
23022
+ if (owner.length > 0)
23023
+ target = owner.length === 1 ? owner[0] : owner;
22560
23024
  }
22561
23025
  } else {
22562
23026
  property = String(firstArg);
@@ -22566,55 +23030,126 @@ var TransitionCommand = (() => {
22566
23030
  }
22567
23031
  if (!raw.modifiers?.to)
22568
23032
  throw new Error('transition requires "to <value>"');
22569
- let value = await evaluator.evaluate(raw.modifiers.to, context);
22570
- if (value === void 0 && raw.modifiers.to.type === "identifier") {
22571
- value = raw.modifiers.to.name;
22572
- }
22573
- const result = { property, value };
23033
+ const result = {
23034
+ property,
23035
+ value: await valueOf(raw.modifiers.to, evaluator, context)
23036
+ };
22574
23037
  if (target !== void 0)
22575
23038
  result.target = target;
23039
+ if (raw.modifiers?.from)
23040
+ result.from = await valueOf(raw.modifiers.from, evaluator, context);
23041
+ if (raw.modifiers?.pairs) {
23042
+ result.pairs = await this.laterPairs(raw.modifiers.pairs, evaluator, context);
23043
+ }
22576
23044
  if (raw.modifiers?.over)
22577
23045
  result.duration = await evaluator.evaluate(raw.modifiers.over, context);
22578
23046
  if (raw.modifiers?.with)
22579
23047
  result.timingFunction = String(await evaluator.evaluate(raw.modifiers.with, context));
23048
+ if (raw.modifiers?.using)
23049
+ result.using = String(await evaluator.evaluate(raw.modifiers.using, context));
22580
23050
  return result;
22581
23051
  }
23052
+ async laterPairs(node, evaluator, context) {
23053
+ const pairs = [];
23054
+ for (const pairNode of node.elements ?? []) {
23055
+ const fields = /* @__PURE__ */ new Map();
23056
+ const properties = pairNode.properties ?? [];
23057
+ for (const { key, value } of properties) {
23058
+ if (typeof key.name === "string")
23059
+ fields.set(key.name, value);
23060
+ }
23061
+ const propertyNode2 = fields.get("property");
23062
+ const toNode = fields.get("to");
23063
+ if (!propertyNode2 || !toNode)
23064
+ throw new Error("transition requires property and value");
23065
+ const pair = {
23066
+ property: String(await evaluator.evaluate(propertyNode2, context)),
23067
+ value: await valueOf(toNode, evaluator, context)
23068
+ };
23069
+ const ownerNode = fields.get("owner");
23070
+ if (ownerNode) {
23071
+ const owners = elementsOf(await evaluator.evaluate(ownerNode, context));
23072
+ if (owners.length === 0)
23073
+ throw new Error("transition: target element not found");
23074
+ pair.target = owners.length === 1 ? owners[0] : owners;
23075
+ }
23076
+ const fromNode = fields.get("from");
23077
+ if (fromNode)
23078
+ pair.from = await valueOf(fromNode, evaluator, context);
23079
+ pairs.push(pair);
23080
+ }
23081
+ return pairs;
23082
+ }
22582
23083
  async execute(input, context) {
22583
- let { property } = input;
22584
- const { target, value, duration: durationInput, timingFunction } = input;
22585
- if (property.startsWith("*"))
22586
- property = property.substring(1);
22587
- property = camelToKebab(property);
22588
- const targetElement = resolveElement(target, context, "transition");
22589
- const duration = parseDuration(durationInput, 300);
22590
- const fromValue = getComputedStyle(targetElement).getPropertyValue(property);
22591
- const originalTransition = targetElement.style.transition;
22592
- const transitionProp = `${property} ${duration}ms ${timingFunction || "ease"}`;
22593
- targetElement.style.transition = originalTransition ? `${originalTransition}, ${transitionProp}` : transitionProp;
22594
- let toValue = String(value);
22595
- let removeInlineAfter = false;
22596
- if (toValue === "initial" || toValue === "inherit" || toValue === "unset" || toValue === "revert") {
22597
- const currentInline = targetElement.style.getPropertyValue(property);
22598
- targetElement.style.removeProperty(property);
22599
- toValue = getComputedStyle(targetElement).getPropertyValue(property);
22600
- if (currentInline) {
22601
- targetElement.style.setProperty(property, currentInline);
22602
- }
22603
- removeInlineAfter = true;
22604
- }
22605
- targetElement.style.setProperty(property, toValue);
22606
- const result = await waitForTransitionEnd(targetElement, property, duration);
22607
- targetElement.style.transition = originalTransition;
22608
- if (removeInlineAfter) {
22609
- targetElement.style.removeProperty(property);
23084
+ const duration = parseDuration(input.duration, input.using !== void 0 && longestTransitionMs(input.using) || 300);
23085
+ const timing = input.timingFunction || "ease";
23086
+ const jobs = [];
23087
+ for (const pair of [input, ...input.pairs ?? []]) {
23088
+ let property = pair.property;
23089
+ if (property.startsWith("*"))
23090
+ property = property.substring(1);
23091
+ property = camelToKebab(property);
23092
+ const elements = Array.isArray(pair.target) ? pair.target.filter(isHTMLElement$2) : [resolveElement(pair.target, context, "transition")];
23093
+ for (const element of elements) {
23094
+ jobs.push({
23095
+ element,
23096
+ property,
23097
+ pair,
23098
+ fromValue: getComputedStyle(element).getPropertyValue(property),
23099
+ toValue: "",
23100
+ removeInlineAfter: false
23101
+ });
23102
+ }
23103
+ }
23104
+ if (jobs.length === 0)
23105
+ throw new Error("transition: target element not found");
23106
+ for (const job of jobs) {
23107
+ if (job.pair.from !== void 0) {
23108
+ job.element.style.setProperty(job.property, String(job.pair.from));
23109
+ }
23110
+ }
23111
+ for (const job of jobs)
23112
+ if (job.pair.from !== void 0)
23113
+ void job.element.offsetWidth;
23114
+ const originals = /* @__PURE__ */ new Map();
23115
+ for (const job of jobs) {
23116
+ if (!originals.has(job.element))
23117
+ originals.set(job.element, job.element.style.transition);
23118
+ }
23119
+ for (const [element, original] of originals) {
23120
+ const own = jobs.filter((job) => job.element === element).map((job) => `${job.property} ${duration}ms ${timing}`);
23121
+ element.style.transition = input.using ?? [original, ...own].filter(Boolean).join(", ");
23122
+ }
23123
+ for (const job of jobs) {
23124
+ const { element, property } = job;
23125
+ let toValue = String(job.pair.value);
23126
+ if (RESTORE_KEYWORDS.has(toValue)) {
23127
+ const currentInline = element.style.getPropertyValue(property);
23128
+ element.style.removeProperty(property);
23129
+ toValue = getComputedStyle(element).getPropertyValue(property);
23130
+ if (currentInline) {
23131
+ element.style.setProperty(property, currentInline);
23132
+ }
23133
+ job.removeInlineAfter = true;
23134
+ }
23135
+ job.toValue = toValue;
23136
+ element.style.setProperty(property, toValue);
22610
23137
  }
23138
+ const results = await Promise.all(jobs.map((job) => waitForTransitionEnd(job.element, job.property, duration)));
23139
+ for (const [element, original] of originals)
23140
+ element.style.transition = original;
23141
+ for (const job of jobs) {
23142
+ if (job.removeInlineAfter)
23143
+ job.element.style.removeProperty(job.property);
23144
+ }
23145
+ const first = jobs[0];
22611
23146
  return {
22612
- element: targetElement,
22613
- property,
22614
- fromValue,
22615
- toValue,
23147
+ element: first.element,
23148
+ property: first.property,
23149
+ fromValue: first.fromValue,
23150
+ toValue: first.toValue,
22616
23151
  duration,
22617
- completed: result.completed
23152
+ completed: results.every((result) => result.completed)
22618
23153
  };
22619
23154
  }
22620
23155
  };
@@ -22627,13 +23162,16 @@ var TransitionCommand = (() => {
22627
23162
  })();
22628
23163
  _classThis.metadata = commandMeta({
22629
23164
  description: "Animate CSS properties using CSS transitions",
22630
- syntax: "transition [<target>] <property> to <value> [over <duration>] [with <timing>]",
23165
+ syntax: "transition <property> [from <value>] to <value> [<property> \u2026 to <value>]\u2026 [over <duration>] [using <css> | with <timing>]",
22631
23166
  examples: [
22632
23167
  "transition opacity to 0.5",
22633
23168
  "transition my *opacity to 0 over 200ms",
22634
23169
  "transition #box's *opacity to 0 over 200ms",
22635
23170
  "transition left to 100px over 500ms",
22636
- "transition background-color to red over 1s with ease-in-out"
23171
+ "transition background-color to red over 1s with ease-in-out",
23172
+ "transition *width to 100px *height to 50px over 300ms",
23173
+ "transition *opacity of #panel from 0 to 1 over 200ms",
23174
+ 'transition my *opacity to 0 using "opacity 1s ease-in"'
22637
23175
  ],
22638
23176
  sideEffects: ["style-change", "timing"],
22639
23177
  category: "animation",
@@ -23834,17 +24372,7 @@ var BeepCommand = (() => {
23834
24372
  this.debugContext(context);
23835
24373
  return { expressionCount: 0, debugged: true, outputs: [] };
23836
24374
  }
23837
- const outputs = [];
23838
- console.group("\u{1F514} beep! Debug Output");
23839
- for (const expression of expressions) {
23840
- const output = this.debugExpression(expression);
23841
- outputs.push(output);
23842
- console.log(`Value:`, expression);
23843
- console.log(`Type:`, output.type);
23844
- console.log(`Representation:`, output.representation);
23845
- console.log("---");
23846
- }
23847
- console.groupEnd();
24375
+ const outputs = beepValues(context.me, expressions);
23848
24376
  return { expressionCount: expressions.length, debugged: true, outputs };
23849
24377
  }
23850
24378
  debugContext(context) {
@@ -23857,63 +24385,6 @@ var BeepCommand = (() => {
23857
24385
  console.log("variables:", context.variables);
23858
24386
  console.groupEnd();
23859
24387
  }
23860
- debugExpression(expression) {
23861
- return {
23862
- value: expression,
23863
- type: this.getType(expression),
23864
- representation: this.getRepresentation(expression)
23865
- };
23866
- }
23867
- getType(value) {
23868
- if (value === null)
23869
- return "null";
23870
- if (value === void 0)
23871
- return "undefined";
23872
- if (Array.isArray(value))
23873
- return "array";
23874
- if (isHTMLElement$2(value))
23875
- return "HTMLElement";
23876
- if (value instanceof Element)
23877
- return "Element";
23878
- if (value instanceof Node)
23879
- return "Node";
23880
- if (value instanceof Error)
23881
- return "Error";
23882
- if (value instanceof Date)
23883
- return "Date";
23884
- if (value instanceof RegExp)
23885
- return "RegExp";
23886
- return typeof value;
23887
- }
23888
- getRepresentation(value) {
23889
- if (value === null)
23890
- return "null";
23891
- if (value === void 0)
23892
- return "undefined";
23893
- if (Array.isArray(value)) {
23894
- return `Array(${value.length}) [${value.slice(0, 3).map((v2) => this.getRepresentation(v2)).join(", ")}${value.length > 3 ? "..." : ""}]`;
23895
- }
23896
- if (isHTMLElement$2(value)) {
23897
- const el = value;
23898
- const tag = el.tagName.toLowerCase();
23899
- const id = el.id ? `#${el.id}` : "";
23900
- const classes = el.className ? `.${el.className.split(" ").join(".")}` : "";
23901
- return `<${tag}${id}${classes}/>`;
23902
- }
23903
- if (value instanceof Error)
23904
- return `Error: ${value.message}`;
23905
- if (typeof value === "string")
23906
- return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
23907
- if (typeof value === "object") {
23908
- try {
23909
- const keys = Object.keys(value);
23910
- return `Object {${keys.slice(0, 3).join(", ")}${keys.length > 3 ? "..." : ""}}`;
23911
- } catch {
23912
- return "[Object]";
23913
- }
23914
- }
23915
- return String(value);
23916
- }
23917
24388
  };
23918
24389
  __setFunctionName(_classThis, "BeepCommand");
23919
24390
  (() => {
@@ -24437,16 +24908,12 @@ var RenderCommand = class _RenderCommand {
24437
24908
  };
24438
24909
  RenderCommand.metadata = commandMeta({
24439
24910
  description: "Render templates with @if, @else, and @repeat directives",
24440
- syntax: [
24441
- "render <template>",
24442
- "render <template> with <variables>",
24443
- "render <template> with (key: value, ...)"
24444
- ],
24911
+ syntax: ["render <template>", "render <template> with <name>: <value>, ..."],
24445
24912
  examples: [
24446
24913
  "render myTemplate",
24447
- 'render myTemplate with (name: "Alice")',
24448
- 'render "<template>Hello ${name}!</template>" with (name: "World")',
24449
- "render template with (items: data)"
24914
+ 'render myTemplate with name: "Alice"',
24915
+ 'render "<template>Hello ${name}!</template>" with name: "World"',
24916
+ "render template with items: data"
24450
24917
  ],
24451
24918
  category: "templates",
24452
24919
  compatibility: "standard",
@@ -24617,7 +25084,7 @@ function updateDebugStats(event) {
24617
25084
  }
24618
25085
  stats.averageConfidence = stats.confidenceHistory.reduce((a, b) => a + b, 0) / stats.confidenceHistory.length;
24619
25086
  }
24620
- var VERSION = "3.1.1";
25087
+ var VERSION = "3.2.0";
24621
25088
  function createHistorySwap(config2) {
24622
25089
  const { target, strategy = "morph", useViewTransition = false, fetchOptions = {}, transformUrl, onBeforeFetch, onAfterSwap, onError } = config2;
24623
25090
  const resolveTarget = () => {
@@ -26125,6 +26592,14 @@ function convertNode(node, infer) {
26125
26592
  ...pos(node)
26126
26593
  };
26127
26594
  case "identifier":
26595
+ if (node.scope === "element" || node.scope === "local" || node.scope === "global") {
26596
+ return {
26597
+ type: "variable",
26598
+ name: node.name ?? "",
26599
+ scope: node.scope,
26600
+ ...pos(node)
26601
+ };
26602
+ }
26128
26603
  return {
26129
26604
  type: "identifier",
26130
26605
  value: node.name ?? node.value ?? "",
@@ -26147,6 +26622,15 @@ function convertNode(node, infer) {
26147
26622
  operand: convertNode(node.argument ?? node.operand, infer),
26148
26623
  ...pos(node)
26149
26624
  };
26625
+ case "typeCheckExpression":
26626
+ return {
26627
+ type: "typeCheck",
26628
+ value: convertNode(node.value, infer),
26629
+ typeName: String(node.typeName),
26630
+ nullOk: node.nullOk !== false,
26631
+ negated: node.negated === true,
26632
+ ...pos(node)
26633
+ };
26150
26634
  case "timeExpression":
26151
26635
  return { type: "literal", value: node.value, ...pos(node) };
26152
26636
  case "templateLiteral":
@@ -26158,6 +26642,28 @@ function convertNode(node, infer) {
26158
26642
  scope: node.scope ?? "local",
26159
26643
  ...pos(node)
26160
26644
  };
26645
+ case "arrayLiteral":
26646
+ return {
26647
+ type: "array",
26648
+ elements: (node.elements ?? []).map((element) => convertNode(element, infer)),
26649
+ ...pos(node)
26650
+ };
26651
+ case "attributeAccess":
26652
+ return {
26653
+ type: "possessive",
26654
+ object: { type: "identifier", value: "me" },
26655
+ property: `@${node.attributeName}`,
26656
+ ...pos(node)
26657
+ };
26658
+ case "propertyOfExpression": {
26659
+ const property = node.property;
26660
+ return {
26661
+ type: "possessive",
26662
+ object: convertNode(node.target, infer),
26663
+ property: property?.name ?? property?.value ?? "",
26664
+ ...pos(node)
26665
+ };
26666
+ }
26161
26667
  case "htmlSelector":
26162
26668
  return {
26163
26669
  type: "selector",
@@ -26208,6 +26714,18 @@ function convertCommand$1(node, infer) {
26208
26714
  if (name === "repeat") {
26209
26715
  return convertRepeatCommand(node, infer);
26210
26716
  }
26717
+ const original = node.originalCommand;
26718
+ const sum = node.modifiers?.to;
26719
+ const counted = node.args?.[0];
26720
+ if (name === "set" && (original === "increment" || original === "decrement") && sum?.type === "binaryExpression" && counted) {
26721
+ return convertCommand$1({
26722
+ ...node,
26723
+ name: original,
26724
+ args: [counted],
26725
+ modifiers: { by: sum.right },
26726
+ originalCommand: void 0
26727
+ }, infer);
26728
+ }
26211
26729
  const args = (node.args ?? []).map((arg) => convertNode(arg, infer));
26212
26730
  const target = node.target ? convertNode(node.target, infer) : void 0;
26213
26731
  const modifiers = node.modifiers ? convertModifiers(node.modifiers, infer) : void 0;
@@ -26249,6 +26767,64 @@ function convertIfCommand(node, infer) {
26249
26767
  };
26250
26768
  }
26251
26769
  function convertRepeatCommand(node, infer) {
26770
+ const slots = node.modifiers ?? {};
26771
+ const form = slotText(slots.loopType);
26772
+ if (form === void 0)
26773
+ return convertPositionalRepeat(node, infer);
26774
+ const [bodyBlock, elseBlock] = node.args ?? [];
26775
+ const body = extractBlockCommands(bodyBlock, infer);
26776
+ const indexName = slotText(slots.index);
26777
+ const shared = {
26778
+ ...indexName ? { indexName } : {},
26779
+ ...elseBlock ? { elseBody: extractBlockCommands(elseBlock, infer) } : {},
26780
+ ...pos(node)
26781
+ };
26782
+ const operand = (slot) => slot ? convertNode(slot, infer) : void 0;
26783
+ switch (form) {
26784
+ case "for":
26785
+ return {
26786
+ type: "foreach",
26787
+ itemName: slotText(slots.for) ?? "it",
26788
+ collection: operand(slots.in) ?? { type: "literal", value: null },
26789
+ body,
26790
+ ...shared
26791
+ };
26792
+ case "times":
26793
+ return { type: "repeat", count: operand(slots.times), body, ...shared };
26794
+ case "while":
26795
+ case "until": {
26796
+ const test = operand(slots[form]) ?? { type: "literal", value: form === "until" };
26797
+ return {
26798
+ type: "while",
26799
+ condition: form === "until" ? { type: "unary", operator: "not", operand: test } : test,
26800
+ ...slots.bottomTested?.value === true ? { bottomTested: true } : {},
26801
+ body,
26802
+ ...shared
26803
+ };
26804
+ }
26805
+ case "until-event": {
26806
+ const event = slotText(slots.event);
26807
+ if (!event) {
26808
+ return { type: "error", message: "'repeat until event' has no event name", ...pos(node) };
26809
+ }
26810
+ const target = operand(slots.from);
26811
+ return {
26812
+ type: "repeat",
26813
+ untilEvent: event,
26814
+ ...target ? { untilEventTarget: target } : {},
26815
+ body,
26816
+ ...shared
26817
+ };
26818
+ }
26819
+ default:
26820
+ return { type: "repeat", body, ...shared };
26821
+ }
26822
+ }
26823
+ function slotText(slot) {
26824
+ const text = slot?.value ?? slot?.name;
26825
+ return typeof text === "string" ? text : void 0;
26826
+ }
26827
+ function convertPositionalRepeat(node, infer) {
26252
26828
  const args = node.args ?? [];
26253
26829
  if (args.length === 0) {
26254
26830
  return { type: "repeat", body: [], ...pos(node) };
@@ -26368,13 +26944,13 @@ function buildEventModifiers(node) {
26368
26944
  ...node.selector && !node.from ? { from: node.selector } : {}
26369
26945
  };
26370
26946
  }
26371
- function extractBlockCommands(block, infer) {
26372
- if (!block)
26947
+ function extractBlockCommands(block2, infer) {
26948
+ if (!block2)
26373
26949
  return [];
26374
- if (block.type === "block") {
26375
- return (block.commands ?? []).map((cmd) => convertNode(cmd, infer));
26950
+ if (block2.type === "block") {
26951
+ return (block2.commands ?? []).map((cmd) => convertNode(cmd, infer));
26376
26952
  }
26377
- return [convertNode(block, infer)];
26953
+ return [convertNode(block2, infer)];
26378
26954
  }
26379
26955
  function inferRoles(name, args, modifiers, target, infer) {
26380
26956
  const roles = {};
@@ -26535,6 +27111,15 @@ function toCoreAST(node) {
26535
27111
  prefix: true,
26536
27112
  ...nodePos(node)
26537
27113
  };
27114
+ case "typeCheck":
27115
+ return {
27116
+ type: "typeCheckExpression",
27117
+ value: toCoreAST(node.value),
27118
+ typeName: node.typeName,
27119
+ nullOk: node.nullOk,
27120
+ negated: node.negated,
27121
+ ...nodePos(node)
27122
+ };
26538
27123
  case "member":
26539
27124
  return {
26540
27125
  type: "memberExpression",
@@ -26543,6 +27128,12 @@ function toCoreAST(node) {
26543
27128
  computed: node.computed ?? false,
26544
27129
  ...nodePos(node)
26545
27130
  };
27131
+ case "array":
27132
+ return {
27133
+ type: "arrayLiteral",
27134
+ elements: node.elements.map((element) => toCoreAST(element)),
27135
+ ...nodePos(node)
27136
+ };
26546
27137
  case "possessive":
26547
27138
  return {
26548
27139
  type: "possessiveExpression",
@@ -26602,67 +27193,79 @@ function convertCommand(node) {
26602
27193
  }
26603
27194
  return result;
26604
27195
  }
27196
+ function block(nodes) {
27197
+ return { type: "block", commands: nodes.map((n) => toCoreAST(n)), ...POS };
27198
+ }
26605
27199
  function convertIf(node) {
26606
- const result = {
27200
+ const [elseIf, ...laterElseIfs] = node.elseIfBranches ?? [];
27201
+ const elseBranch = elseIf ? [
27202
+ {
27203
+ type: "if",
27204
+ condition: elseIf.condition,
27205
+ thenBranch: elseIf.body,
27206
+ elseIfBranches: laterElseIfs,
27207
+ ...node.elseBranch ? { elseBranch: node.elseBranch } : {}
27208
+ }
27209
+ ] : node.elseBranch;
27210
+ return {
26607
27211
  type: "command",
26608
27212
  name: "if",
26609
27213
  isBlocking: true,
26610
- condition: toCoreAST(node.condition),
26611
- thenBranch: node.thenBranch.map((n) => toCoreAST(n)),
26612
- args: [],
27214
+ args: [
27215
+ toCoreAST(node.condition),
27216
+ block(node.thenBranch),
27217
+ ...elseBranch ? [block(elseBranch)] : []
27218
+ ],
26613
27219
  ...nodePos(node)
26614
27220
  };
26615
- if (node.elseBranch) {
26616
- result.elseBranch = node.elseBranch.map((n) => toCoreAST(n));
26617
- }
26618
- return result;
26619
27221
  }
26620
- function convertRepeat(node) {
26621
- const result = {
27222
+ function loopCommand(node, form, operands) {
27223
+ const modifiers = { loopType: slotString(form) };
27224
+ for (const [slot, value] of Object.entries(operands)) {
27225
+ if (value)
27226
+ modifiers[slot] = value;
27227
+ }
27228
+ if (node.indexName)
27229
+ modifiers.index = slotString(node.indexName);
27230
+ return {
26622
27231
  type: "command",
26623
27232
  name: "repeat",
26624
27233
  isBlocking: true,
26625
- body: node.body.map((n) => toCoreAST(n)),
26626
- args: [],
27234
+ args: [block(node.body), ...node.elseBody ? [block(node.elseBody)] : []],
27235
+ modifiers,
26627
27236
  ...nodePos(node)
26628
27237
  };
27238
+ }
27239
+ function slotString(value) {
27240
+ return { type: "string", value, ...POS };
27241
+ }
27242
+ function convertRepeat(node) {
26629
27243
  if (node.count !== void 0) {
26630
- result.count = typeof node.count === "number" ? { type: "literal", value: node.count, raw: String(node.count), ...POS } : toCoreAST(node.count);
26631
- result.loopVariant = "times";
26632
- } else if (node.whileCondition !== void 0) {
26633
- result.condition = toCoreAST(node.whileCondition);
26634
- result.loopVariant = "while";
26635
- } else if (node.untilEvent !== void 0) {
26636
- result.untilEvent = node.untilEvent;
26637
- result.loopVariant = "until";
27244
+ const times = typeof node.count === "number" ? { type: "literal", value: node.count, raw: String(node.count), ...POS } : toCoreAST(node.count);
27245
+ return loopCommand(node, "times", { times });
26638
27246
  }
26639
- return result;
27247
+ if (node.whileCondition !== void 0) {
27248
+ return loopCommand(node, "while", { while: toCoreAST(node.whileCondition) });
27249
+ }
27250
+ if (node.untilEvent !== void 0) {
27251
+ return loopCommand(node, "until-event", {
27252
+ event: slotString(node.untilEvent),
27253
+ from: node.untilEventTarget ? toCoreAST(node.untilEventTarget) : void 0
27254
+ });
27255
+ }
27256
+ return loopCommand(node, "forever", {});
26640
27257
  }
26641
27258
  function convertForEach(node) {
26642
- return {
26643
- type: "command",
26644
- name: "repeat",
26645
- isBlocking: true,
26646
- loopVariant: "for",
26647
- itemName: node.itemName,
26648
- ...node.indexName ? { indexName: node.indexName } : {},
26649
- collection: toCoreAST(node.collection),
26650
- body: node.body.map((n) => toCoreAST(n)),
26651
- args: [],
26652
- ...nodePos(node)
26653
- };
27259
+ return loopCommand(node, "for", {
27260
+ for: slotString(node.itemName),
27261
+ in: toCoreAST(node.collection)
27262
+ });
26654
27263
  }
26655
27264
  function convertWhile(node) {
26656
- return {
26657
- type: "command",
26658
- name: "repeat",
26659
- isBlocking: true,
26660
- loopVariant: "while",
26661
- condition: toCoreAST(node.condition),
26662
- body: node.body.map((n) => toCoreAST(n)),
26663
- args: [],
26664
- ...nodePos(node)
26665
- };
27265
+ return loopCommand(node, "while", {
27266
+ while: toCoreAST(node.condition),
27267
+ bottomTested: node.bottomTested ? { type: "literal", value: true, raw: "true", ...POS } : void 0
27268
+ });
26666
27269
  }
26667
27270
  function flattenEventModifiers(modifiers) {
26668
27271
  if (!modifiers)
@@ -27071,4 +27674,4 @@ export {
27071
27674
  toCoreAST,
27072
27675
  validatePartialContent
27073
27676
  };
27074
- //# sourceMappingURL=dist-HOHQFWDA.js.map
27677
+ //# sourceMappingURL=dist-SO754VTX.js.map