bullet_train 1.0.76 → 1.0.83
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/concerns/account/controllers/base.rb +9 -0
- data/app/controllers/concerns/account/teams/controller_base.rb +1 -1
- data/app/helpers/base_helper.rb +5 -1
- data/app/models/concerns/memberships/base.rb +5 -0
- data/app/models/concerns/records/base.rb +8 -0
- data/app/models/concerns/teams/base.rb +19 -0
- data/config/locales/en/billing/products.en.yml +17 -0
- data/lib/bullet_train/version.rb +1 -1
- data/lib/bullet_train.rb +6 -1
- metadata +4 -5
- data/app/assets/javascripts/bullet-train.js +0 -2
- data/app/assets/javascripts/bullet-train.js.map +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2119a17f5ed1aace492642ab674c8fb67cadefd29100aee3809e590813da3bb
|
4
|
+
data.tar.gz: '0679c15de2ec6260be9beab3c1261fbef4892cacd961d285f813229aac5b1616'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 807684f8b6d5a99a6d35a78d0e50cbeb8a0ef40d06b1e56c1c653a6851ea0289e50c75f2a1e75a1500f6799f3fcd91a603fffaa64ea12324068a851290e67186
|
7
|
+
data.tar.gz: 964b44f5ddaa872e26e8cefcf0dd6f9a11d19fc7d3b435e10e7a14d6f62c420a60945dde1eb0c9c588f049cd64423e93c82c52e2b207c01c2cfbe7d58c5b125b
|
@@ -5,6 +5,10 @@ module Account::Controllers::Base
|
|
5
5
|
include LoadsAndAuthorizesResource
|
6
6
|
include Fields::ControllerSupport
|
7
7
|
|
8
|
+
if billing_enabled?
|
9
|
+
include Billing::ControllerSupport
|
10
|
+
end
|
11
|
+
|
8
12
|
before_action :set_last_seen_at, if: proc {
|
9
13
|
user_signed_in? && (current_user.last_seen_at.nil? || current_user.last_seen_at < 1.minute.ago)
|
10
14
|
}
|
@@ -106,6 +110,11 @@ module Account::Controllers::Base
|
|
106
110
|
end
|
107
111
|
end
|
108
112
|
|
113
|
+
# TODO Maybe in this context we should check whether `Billing::ControllerSupport` is included instead of just defined?
|
114
|
+
if defined?(Billing::ControllerSupport)
|
115
|
+
enforce_billing_requirements
|
116
|
+
# See `app/controllers/concerns/billing_support.rb` for details.
|
117
|
+
end
|
109
118
|
end
|
110
119
|
|
111
120
|
true
|
@@ -81,7 +81,7 @@ module Account::Teams::ControllerBase
|
|
81
81
|
format.html { redirect_to [:account, @team], notice: I18n.t("teams.notifications.created") }
|
82
82
|
format.json { render :show, status: :created, location: [:account, @team] }
|
83
83
|
else
|
84
|
-
format.html { render :new, layout: "devise" }
|
84
|
+
format.html { render :new, layout: "devise", status: :unprocessable_entity }
|
85
85
|
format.json { render json: @team.errors, status: :unprocessable_entity }
|
86
86
|
end
|
87
87
|
end
|
data/app/helpers/base_helper.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
module BaseHelper
|
2
2
|
# TODO This is for the billing package to override, but I feel like there has got to be a better way to do this.
|
3
3
|
def hide_team_resource_menus?
|
4
|
-
|
4
|
+
if billing_enabled?
|
5
|
+
current_team.needs_billing_subscription?
|
6
|
+
else
|
7
|
+
false
|
8
|
+
end
|
5
9
|
end
|
6
10
|
end
|
@@ -29,6 +29,11 @@ module Memberships::Base
|
|
29
29
|
scope :current_and_invited, -> { includes(:invitation).where("user_id IS NOT NULL OR invitations.id IS NOT NULL").references(:invitation) }
|
30
30
|
scope :current, -> { where("user_id IS NOT NULL") }
|
31
31
|
scope :tombstones, -> { includes(:invitation).where("user_id IS NULL AND invitations.id IS NULL").references(:invitation) }
|
32
|
+
|
33
|
+
# TODO Probably we can provide a way for gem packages to define these kinds of extensions.
|
34
|
+
if billing_enabled?
|
35
|
+
scope :billable, -> { current }
|
36
|
+
end
|
32
37
|
end
|
33
38
|
|
34
39
|
def name
|
@@ -28,6 +28,14 @@ module Records::Base
|
|
28
28
|
scope :newest_updated, -> { order("updated_at DESC") }
|
29
29
|
scope :oldest_updated, -> { order("updated_at ASC") }
|
30
30
|
|
31
|
+
# TODO Probably we can provide a way for gem packages to define these kinds of extensions.
|
32
|
+
if billing_enabled?
|
33
|
+
# By default, any model in a collection is considered active for billing purposes.
|
34
|
+
# This can be overloaded in the child model class to specify more specific criteria for billing.
|
35
|
+
# See `app/models/concerns/memberships/base.rb` for an example.
|
36
|
+
scope :billable, -> { order("TRUE") }
|
37
|
+
end
|
38
|
+
|
31
39
|
# Microscope adds useful scopes targeting ActiveRecord `boolean`, `date` and `datetime` attributes.
|
32
40
|
# https://github.com/mirego/microscope
|
33
41
|
acts_as_microscope
|
@@ -18,6 +18,17 @@ module Teams::Base
|
|
18
18
|
# integrations
|
19
19
|
has_many :integrations_stripe_installations, class_name: "Integrations::StripeInstallation", dependent: :destroy if stripe_enabled?
|
20
20
|
|
21
|
+
# TODO Probably we can provide a way for gem packages to define these kinds of extensions.
|
22
|
+
if billing_enabled?
|
23
|
+
# subscriptions
|
24
|
+
has_many :billing_subscriptions, class_name: "Billing::Subscription", dependent: :destroy, foreign_key: :team_id
|
25
|
+
|
26
|
+
# TODO We need a way for `bullet_train-billing-stripe` to define these.
|
27
|
+
if defined?(Billing::Stripe::Subscription)
|
28
|
+
has_many :billing_stripe_subscriptions, class_name: "Billing::Stripe::Subscription", dependent: :destroy, foreign_key: :team_id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
21
32
|
# validations
|
22
33
|
validates :name, presence: true
|
23
34
|
validates :time_zone, inclusion: {in: ActiveSupport::TimeZone.all.map(&:name)}, allow_nil: true
|
@@ -48,4 +59,12 @@ module Teams::Base
|
|
48
59
|
# generic functions need to function for a team model as well, so we do this.
|
49
60
|
self
|
50
61
|
end
|
62
|
+
|
63
|
+
# TODO Probably we can provide a way for gem packages to define these kinds of extensions.
|
64
|
+
if billing_enabled?
|
65
|
+
def needs_billing_subscription?
|
66
|
+
return false if freemium_enabled?
|
67
|
+
billing_subscriptions.active.empty?
|
68
|
+
end
|
69
|
+
end
|
51
70
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
en:
|
2
|
+
billing/products:
|
3
|
+
free:
|
4
|
+
name: Free
|
5
|
+
basic:
|
6
|
+
name: Basic
|
7
|
+
description: This is an example plan that includes a free trial when paid for monthly.
|
8
|
+
features:
|
9
|
+
- Demonstrates pricing per team member.
|
10
|
+
- Allows creation of up to fifty "Creative Concepts".
|
11
|
+
- Soft enforcement that limit.
|
12
|
+
pro:
|
13
|
+
name: Pro
|
14
|
+
description: An improved example plan that demonstrates different features.
|
15
|
+
features:
|
16
|
+
- Demonstrates a fixed price for up to ten team members.
|
17
|
+
- Allows creation of an unlimited number of "Creative Concepts".
|
data/lib/bullet_train/version.rb
CHANGED
data/lib/bullet_train.rb
CHANGED
@@ -61,7 +61,12 @@ def inbound_email_enabled?
|
|
61
61
|
ENV["INBOUND_EMAIL_DOMAIN"].present?
|
62
62
|
end
|
63
63
|
|
64
|
-
def
|
64
|
+
def billing_enabled?
|
65
|
+
defined?(BulletTrain::Billing)
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO This should be in an initializer or something.
|
69
|
+
def billing_subscription_creation_disabled?
|
65
70
|
false
|
66
71
|
end
|
67
72
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullet_train
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.83
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Culver
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-06-
|
11
|
+
date: 2022-06-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: standard
|
@@ -428,8 +428,6 @@ files:
|
|
428
428
|
- README.md
|
429
429
|
- Rakefile
|
430
430
|
- app/assets/config/bullet_train_manifest.js
|
431
|
-
- app/assets/javascripts/bullet-train.js
|
432
|
-
- app/assets/javascripts/bullet-train.js.map
|
433
431
|
- app/controllers/account/invitations_controller.rb
|
434
432
|
- app/controllers/account/memberships_controller.rb
|
435
433
|
- app/controllers/account/onboarding/user_details_controller.rb
|
@@ -558,6 +556,7 @@ files:
|
|
558
556
|
- config/initializers/concerns/inflections_base.rb
|
559
557
|
- config/initializers/concerns/turbo_failure_app.rb
|
560
558
|
- config/locales/en/base.yml
|
559
|
+
- config/locales/en/billing/products.en.yml
|
561
560
|
- config/locales/en/devise.en.yml
|
562
561
|
- config/locales/en/doorkeeper.en.yml
|
563
562
|
- config/locales/en/invitations.en.yml
|
@@ -667,7 +666,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
667
666
|
- !ruby/object:Gem::Version
|
668
667
|
version: '0'
|
669
668
|
requirements: []
|
670
|
-
rubygems_version: 3.
|
669
|
+
rubygems_version: 3.2.22
|
671
670
|
signing_key:
|
672
671
|
specification_version: 4
|
673
672
|
summary: Bullet Train
|
@@ -1,2 +0,0 @@
|
|
1
|
-
import{Controller as e}from"@hotwired/stimulus";function t(e){const t=(e.match(/^(?:\.\/)?(.+)(?:[_-]controller\..+?)$/)||[])[1];if(t)return t.replace(/_/g,"-").replace(/\//g,"--")}class i extends e{copy(){this.inputTarget.value=this.sourceTarget.innerText,this.inputTarget.select(),document.execCommand("copy"),this.buttonTarget.innerHTML='<i id="copied" class="fas fa-check w-4 h-4 block text-green-600"></i>',setTimeout(function(){document.getElementById("copied").innerHTML='<i class="far fa-copy w-4 h-4 block text-gray-600"></i>'},1500)}}i.targets=["source","input","button"];class r extends e{constructor(){super(...arguments),this.removeTrailingNewlines=e=>{e.element.innerHTML.match(/<br><\/div>$/)&&(e.element.innerHTML=e.element.innerHTML.slice(0,-10)+"</div>",this.removeTrailingNewlines(e))},this.removeTrailingWhitespace=e=>{e.element.innerHTML.match(/ <\/div>$/)?(e.element.innerHTML=e.element.innerHTML.slice(0,-12)+"</div>",this.removeTrailingWhitespace(e)):e.element.innerHTML.match(/ <\/div>$/)&&(e.element.innerHTML=e.element.innerHTML.slice(0,-13)+"</div>",this.removeTrailingWhitespace(e))}}resetOnSuccess(e){e.detail.success&&e.target.reset()}stripTrix(){this.trixFieldTargets.forEach(e=>{this.removeTrailingNewlines(e.editor),this.removeTrailingWhitespace(e.editor),e.parentElement.querySelector("input").value=e.innerHTML})}submitOnReturn(e){if((e.metaKey||e.ctrlKey)&&13==e.keyCode){e.preventDefault();let t=e.target.closest("form");this.submitForm(t)}}submitForm(e){e.requestSubmit?e.requestSubmit():e.querySelector("[type=submit]").click()}}r.targets=["trixField","scroll"];class s extends e{toggle(){const e=this.isWrapperHidden?this.showEventNameValue:this.hideEventNameValue;this.isWrapperHidden&&this.showWrapper(),this.wrapperTarget.dispatchEvent(new CustomEvent(e))}get isWrapperHidden(){return this.wrapperTarget.classList.contains(this.hiddenClass)}showWrapper(){this.wrapperTarget.classList.remove(this.hiddenClass)}hideWrapper(){this.wrapperTarget.classList.add(this.hiddenClass)}}s.targets=["wrapper"],s.classes=["hidden"],s.values={showEventName:String,hideEventName:String};const n=[[i,"clipboard_controller.js"],[r,"form_controller.js"],[s,"mobile_menu_controller.js"]].map(function(e){const i=e[0];return{identifier:t(e[1]),controllerConstructor:i}});document.addEventListener("turbo:load",()=>{navigator.userAgent.toLocaleLowerCase().includes("electron")&&document.body.classList.add("electron")});export{n as controllerDefinitions};
|
2
|
-
//# sourceMappingURL=bullet-train.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"bullet-train.js","sources":["../../../node_modules/@hotwired/stimulus-webpack-helpers/dist/stimulus-webpack-helpers.js","../../javascript/controllers/clipboard_controller.js","../../javascript/controllers/form_controller.js","../../javascript/controllers/mobile_menu_controller.js","../../javascript/controllers/index.js","../../javascript/electron/index.js"],"sourcesContent":["/*\nStimulus Webpack Helpers 1.0.0\nCopyright © 2021 Basecamp, LLC\n */\nfunction definitionsFromContext(context) {\n return context.keys()\n .map((key) => definitionForModuleWithContextAndKey(context, key))\n .filter((value) => value);\n}\nfunction definitionForModuleWithContextAndKey(context, key) {\n const identifier = identifierForContextKey(key);\n if (identifier) {\n return definitionForModuleAndIdentifier(context(key), identifier);\n }\n}\nfunction definitionForModuleAndIdentifier(module, identifier) {\n const controllerConstructor = module.default;\n if (typeof controllerConstructor == \"function\") {\n return { identifier, controllerConstructor };\n }\n}\nfunction identifierForContextKey(key) {\n const logicalName = (key.match(/^(?:\\.\\/)?(.+)(?:[_-]controller\\..+?)$/) || [])[1];\n if (logicalName) {\n return logicalName.replace(/_/g, \"-\").replace(/\\//g, \"--\");\n }\n}\n\nexport { definitionForModuleAndIdentifier, definitionForModuleWithContextAndKey, definitionsFromContext, identifierForContextKey };\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = ['source', 'input', 'button']\n\n copy() {\n this.inputTarget.value = this.sourceTarget.innerText\n this.inputTarget.select()\n document.execCommand('copy')\n this.buttonTarget.innerHTML = '<i id=\"copied\" class=\"fas fa-check w-4 h-4 block text-green-600\"></i>'\n setTimeout(function () {\n document.getElementById('copied').innerHTML = '<i class=\"far fa-copy w-4 h-4 block text-gray-600\"></i>'\n }, 1500)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\n// TODO Some of this feels really specific to the conversation messages form. Should we rename this controller?\nexport default class extends Controller {\n static targets = ['trixField', 'scroll']\n\n resetOnSuccess(e){\n if(e.detail.success) {\n e.target.reset();\n }\n }\n\n stripTrix(){\n this.trixFieldTargets.forEach(element => {\n this.removeTrailingNewlines(element.editor)\n this.removeTrailingWhitespace(element.editor)\n // When doing this as part of the form submission, Trix does not update the input element's value attribute fast enough.\n // In order to submit the stripped value, we manually update it here to fix the race condition\n element.parentElement.querySelector(\"input\").value = element.innerHTML\n })\n }\n\n submitOnReturn(e) {\n if((e.metaKey || e.ctrlKey) && e.keyCode == 13) {\n e.preventDefault();\n let form = e.target.closest(\"form\")\n this.submitForm(form)\n }\n }\n\n removeTrailingNewlines = (trixEditor) => {\n if (trixEditor.element.innerHTML.match(/<br><\\/div>$/)) {\n trixEditor.element.innerHTML = trixEditor.element.innerHTML.slice(0, -10) + \"</div>\"\n this.removeTrailingNewlines(trixEditor)\n }\n }\n\n removeTrailingWhitespace = (trixEditor) => {\n if (trixEditor.element.innerHTML.match(/ <\\/div>$/)) {\n trixEditor.element.innerHTML = trixEditor.element.innerHTML.slice(0, -12) + \"</div>\"\n this.removeTrailingWhitespace(trixEditor)\n } else if (trixEditor.element.innerHTML.match(/ <\\/div>$/)) {\n trixEditor.element.innerHTML = trixEditor.element.innerHTML.slice(0, -13) + \"</div>\"\n this.removeTrailingWhitespace(trixEditor)\n }\n }\n\n submitForm(form) {\n // Right now, Safari and IE don't support the requestSubmit method which is required for Turbo\n // Doing form.submit() doesn't actually fire the submit event which Turbo needs\n if (form.requestSubmit) {\n form.requestSubmit()\n } else {\n form.querySelector(\"[type=submit]\").click()\n }\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [ \"wrapper\"]\n static classes = [ \"hidden\" ] // necessary because stimulus-reveal will mess with the [hidden] attribute on the wrapper\n static values = {\n showEventName: String,\n hideEventName: String,\n }\n\n toggle() {\n const eventName = this.isWrapperHidden? this.showEventNameValue: this.hideEventNameValue\n if (this.isWrapperHidden) {\n this.showWrapper()\n }\n \n this.wrapperTarget.dispatchEvent(new CustomEvent(eventName))\n }\n \n get isWrapperHidden() {\n return this.wrapperTarget.classList.contains(this.hiddenClass)\n }\n \n showWrapper() {\n this.wrapperTarget.classList.remove(this.hiddenClass)\n }\n \n hideWrapper() {\n this.wrapperTarget.classList.add(this.hiddenClass)\n }\n}","import { identifierForContextKey } from \"@hotwired/stimulus-webpack-helpers\"\n\nimport ClipboardController from './clipboard_controller'\nimport FormController from './form_controller'\nimport MobileMenuController from './mobile_menu_controller'\n\nexport const controllerDefinitions = [\n [ClipboardController, 'clipboard_controller.js'],\n [FormController, 'form_controller.js'],\n [MobileMenuController, 'mobile_menu_controller.js'],\n].map(function(d) {\n const key = d[1]\n const controller = d[0]\n return {\n identifier: identifierForContextKey(key),\n controllerConstructor: controller\n }\n})\n","document.addEventListener(\"turbo:load\", () => {\n if (navigator.userAgent.toLocaleLowerCase().includes('electron')) {\n document.body.classList.add('electron')\n }\n})"],"names":["identifierForContextKey","key","logicalName","match","replace","Controller","copy","this","inputTarget","value","sourceTarget","innerText","select","document","execCommand","buttonTarget","innerHTML","setTimeout","getElementById","targets","removeTrailingNewlines","trixEditor","element","slice","removeTrailingWhitespace","resetOnSuccess","e","detail","success","target","reset","stripTrix","trixFieldTargets","forEach","editor","parentElement","querySelector","submitOnReturn","metaKey","ctrlKey","keyCode","preventDefault","form","closest","submitForm","requestSubmit","click","toggle","eventName","isWrapperHidden","showEventNameValue","hideEventNameValue","showWrapper","wrapperTarget","dispatchEvent","CustomEvent","classList","contains","hiddenClass","remove","hideWrapper","add","classes","values","showEventName","String","hideEventName","controllerDefinitions","ClipboardController","FormController","MobileMenuController","map","d","controller","identifier","controllerConstructor","addEventListener","navigator","userAgent","toLocaleLowerCase","includes","body"],"mappings":"gDAqBA,SAASA,EAAwBC,GAC7B,MAAMC,GAAeD,EAAIE,MAAM,2CAA6C,IAAI,GAChF,GAAID,EACA,OAAOA,EAAYE,QAAQ,KAAM,KAAKA,QAAQ,MAAO,sBCtBhCC,EAG3BC,OACEC,KAAKC,YAAYC,MAAQF,KAAKG,aAAaC,UAC3CJ,KAAKC,YAAYI,SACjBC,SAASC,YAAY,QACrBP,KAAKQ,aAAaC,UAAY,wEAC9BC,WAAW,WACTJ,SAASK,eAAe,UAAUF,UAAY,2DAC7C,SATEG,QAAU,CAAC,SAAU,QAAS,0BCAVd,yCA2B3Be,uBAA0BC,IACpBA,EAAWC,QAAQN,UAAUb,MAAM,kBACrCkB,EAAWC,QAAQN,UAAYK,EAAWC,QAAQN,UAAUO,MAAM,GAAI,IAAM,SAC5EhB,KAAKa,uBAAuBC,UAIhCG,yBAA4BH,IACtBA,EAAWC,QAAQN,UAAUb,MAAM,mBACrCkB,EAAWC,QAAQN,UAAYK,EAAWC,QAAQN,UAAUO,MAAM,GAAI,IAAM,SAC5EhB,KAAKiB,yBAAyBH,IACrBA,EAAWC,QAAQN,UAAUb,MAAM,qBAC5CkB,EAAWC,QAAQN,UAAYK,EAAWC,QAAQN,UAAUO,MAAM,GAAI,IAAM,SAC5EhB,KAAKiB,yBAAyBH,KArClCI,eAAeC,GACVA,EAAEC,OAAOC,SACVF,EAAEG,OAAOC,QAIbC,YACExB,KAAKyB,iBAAiBC,QAAQX,IAC5Bf,KAAKa,uBAAuBE,EAAQY,QACpC3B,KAAKiB,yBAAyBF,EAAQY,QAGtCZ,EAAQa,cAAcC,cAAc,SAAS3B,MAAQa,EAAQN,YAIjEqB,eAAeX,GACb,IAAIA,EAAEY,SAAWZ,EAAEa,UAA0B,IAAbb,EAAEc,QAAe,CAC/Cd,EAAEe,iBACF,IAAIC,EAAOhB,EAAEG,OAAOc,QAAQ,QAC5BpC,KAAKqC,WAAWF,IAqBpBE,WAAWF,GAGLA,EAAKG,cACPH,EAAKG,gBAELH,EAAKN,cAAc,iBAAiBU,WAjDjC3B,QAAU,CAAC,YAAa,0BCFJd,EAQ3B0C,SACE,MAAMC,EAAYzC,KAAK0C,gBAAiB1C,KAAK2C,mBAAoB3C,KAAK4C,mBAClE5C,KAAK0C,iBACP1C,KAAK6C,cAGP7C,KAAK8C,cAAcC,cAAc,IAAIC,YAAYP,IAG/CC,sBACF,YAAYI,cAAcG,UAAUC,SAASlD,KAAKmD,aAGpDN,cACE7C,KAAK8C,cAAcG,UAAUG,OAAOpD,KAAKmD,aAG3CE,cACErD,KAAK8C,cAAcG,UAAUK,IAAItD,KAAKmD,gBAzBjCvC,QAAU,CAAE,aACZ2C,QAAU,CAAE,YACZC,OAAS,CACdC,cAAeC,OACfC,cAAeD,QCDNE,MAAAA,EAAwB,CACnC,CAACC,EAAqB,2BACtB,CAACC,EAAgB,sBACjB,CAACC,EAAsB,8BACvBC,IAAI,SAASC,GACb,MACMC,EAAaD,EAAE,GACrB,MAAO,CACLE,WAAY1E,EAHFwE,EAAE,IAIZG,sBAAuBF,KCf3B5D,SAAS+D,iBAAiB,aAAc,KAClCC,UAAUC,UAAUC,oBAAoBC,SAAS,aACnDnE,SAASoE,KAAKzB,UAAUK,IAAI"}
|