Support expressions in variables initialization

commit_hash:1af4f28b41a00effe9c777281d3937e9e9581676
This commit is contained in:
grechka62
2025-05-14 19:22:15 +03:00
parent 26d44413f3
commit 01ec3d815d
15 changed files with 119 additions and 143 deletions
@@ -5,6 +5,7 @@ package com.yandex.div
import android.net.Uri
import androidx.annotation.ColorInt
import com.yandex.div.json.expressions.Expression
import com.yandex.div2.ArrayVariable
import com.yandex.div2.BoolVariable
import com.yandex.div2.ColorVariable
@@ -18,33 +19,33 @@ import org.json.JSONArray
import org.json.JSONObject
fun integerVariable(name: String, value: Long): DivVariable.Integer {
return DivVariable.Integer(IntegerVariable(name, value))
return DivVariable.Integer(IntegerVariable(name, Expression.constant(value)))
}
fun numberVariable(name: String, value: Double): DivVariable.Number {
return DivVariable.Number(NumberVariable(name, value))
return DivVariable.Number(NumberVariable(name, Expression.constant(value)))
}
fun boolVariable(name: String, value: Boolean): DivVariable.Bool {
return DivVariable.Bool(BoolVariable(name, value))
return DivVariable.Bool(BoolVariable(name, Expression.constant(value)))
}
fun stringVariable(name: String, value: String): DivVariable.Str {
return DivVariable.Str(StrVariable(name, value))
return DivVariable.Str(StrVariable(name, Expression.constant(value)))
}
fun colorVariable(name: String, @ColorInt value: Int): DivVariable.Color {
return DivVariable.Color(ColorVariable(name, value))
return DivVariable.Color(ColorVariable(name, Expression.constant(value)))
}
fun urlVariable(name: String, value: Uri): DivVariable.Url {
return DivVariable.Url(UrlVariable(name, value))
return DivVariable.Url(UrlVariable(name, Expression.constant(value)))
}
internal fun dictVariable(name: String, value: JSONObject): DivVariable.Dict {
return DivVariable.Dict(DictVariable(name, value))
return DivVariable.Dict(DictVariable(name, Expression.constant(value)))
}
internal fun arrayVariable(name: String, value: JSONArray): DivVariable.Array {
return DivVariable.Array(ArrayVariable(name, value))
return DivVariable.Array(ArrayVariable(name, Expression.constant(value)))
}
@@ -10,6 +10,7 @@ import com.yandex.div.internal.Assert
import com.yandex.div.internal.parser.STRING_TO_COLOR_INT
import com.yandex.div.internal.util.toBoolean
import com.yandex.div.json.JSONSerializable
import com.yandex.div.json.expressions.Expression
import com.yandex.div2.BoolVariable
import org.json.JSONArray
import org.json.JSONException
@@ -321,14 +322,14 @@ sealed class Variable {
fun writeToJSON(): JSONObject {
val serializable: JSONSerializable = when (this) {
is ArrayVariable -> com.yandex.div2.ArrayVariable(this.name, this.value)
is BooleanVariable -> BoolVariable(this.name, this.value)
is ColorVariable -> com.yandex.div2.ColorVariable(this.name, this.value.value)
is DictVariable -> com.yandex.div2.DictVariable(this.name, this.value)
is DoubleVariable -> com.yandex.div2.NumberVariable(this.name, this.value)
is IntegerVariable -> com.yandex.div2.IntegerVariable(this.name, this.value)
is StringVariable -> com.yandex.div2.StrVariable(this.name, this.value)
is UrlVariable -> com.yandex.div2.UrlVariable(this.name, this.value)
is ArrayVariable -> com.yandex.div2.ArrayVariable(this.name, Expression.constant(this.value))
is BooleanVariable -> BoolVariable(this.name, Expression.constant(this.value))
is ColorVariable -> com.yandex.div2.ColorVariable(this.name, Expression.constant(this.value.value))
is DictVariable -> com.yandex.div2.DictVariable(this.name, Expression.constant(this.value))
is DoubleVariable -> com.yandex.div2.NumberVariable(this.name, Expression.constant(this.value))
is IntegerVariable -> com.yandex.div2.IntegerVariable(this.name, Expression.constant(this.value))
is StringVariable -> com.yandex.div2.StrVariable(this.name, Expression.constant(this.value))
is UrlVariable -> com.yandex.div2.UrlVariable(this.name, Expression.constant(this.value))
}
return serializable.writeToJSON()
@@ -20,6 +20,7 @@ import com.yandex.div.data.VariableDeclarationException
import com.yandex.div.evaluable.EvaluationContext
import com.yandex.div.evaluable.Evaluator
import com.yandex.div.evaluable.function.GeneratedBuiltinFunctionProvider
import com.yandex.div.json.expressions.ExpressionResolver
import com.yandex.div2.DivData
import com.yandex.div2.DivVariable
import java.util.Collections
@@ -45,7 +46,7 @@ internal class ExpressionsRuntimeProvider @Inject constructor(
val result = runtimes.getOrPut(tag.id) { createRuntimeFor(data, tag) }
val errorCollector = errorCollectors.getOrCreate(tag, data)
divDataTags.getOrPut(div2View, ::mutableSetOf).add(tag.id)
ensureVariablesSynced(result.variableController, data, errorCollector)
ensureVariablesSynced(result.variableController, result.expressionResolver, data, errorCollector)
result.triggersController?.ensureTriggersSynced(data.variableTriggers ?: emptyList())
return result
}
@@ -69,13 +70,14 @@ internal class ExpressionsRuntimeProvider @Inject constructor(
private fun ensureVariablesSynced(
v: VariableController,
resolver: ExpressionResolver,
data: DivData,
errorCollector: ErrorCollector
) {
data.variables?.forEach {
val existingVariable = v.getMutableVariable(it.name) ?: run {
try {
v.declare(it.toVariable())
v.declare(it.toVariable(resolver))
} catch (e: VariableDeclarationException) {
errorCollector.logError(e)
}
@@ -110,17 +112,8 @@ internal class ExpressionsRuntimeProvider @Inject constructor(
private fun createRuntimeFor(data: DivData, tag: DivDataTag): ExpressionsRuntime {
val errorCollector = errorCollectors.getOrCreate(tag, data)
val variableController = VariableControllerImpl().apply {
data.variables?.forEach { divVariable: DivVariable ->
try {
declare(divVariable.toVariable())
} catch (e: VariableDeclarationException) {
errorCollector.logError(e)
}
}
addSource(divVariableController.variableSource)
}
val variableController = VariableControllerImpl()
variableController.addSource(divVariableController.variableSource)
val functionProvider = FunctionProviderDecorator(GeneratedBuiltinFunctionProvider)
val evaluationContext = EvaluationContext(
@@ -155,6 +148,14 @@ internal class ExpressionsRuntimeProvider @Inject constructor(
onCreateCallback = callback,
)
data.variables?.forEach { divVariable: DivVariable ->
try {
variableController.declare(divVariable.toVariable(expressionResolver))
} catch (e: VariableDeclarationException) {
errorCollector.logError(e)
}
}
val triggersController = TriggersController(
variableController,
expressionResolver,
@@ -122,7 +122,7 @@ internal class DivRuntimeVisitor @Inject constructor(
div: Div,
divView: Div2View,
path: String,
parentRuntime: ExpressionsRuntime?
parentRuntime: ExpressionsRuntime
): ExpressionsRuntime? {
if (!div.needLocalRuntime) return parentRuntime
@@ -6,20 +6,18 @@ import com.yandex.div.core.ObserverList
import com.yandex.div.core.expression.ExpressionResolverImpl
import com.yandex.div.core.expression.ExpressionsRuntime
import com.yandex.div.core.expression.triggers.TriggersController
import com.yandex.div.core.expression.variables.VariableController
import com.yandex.div.core.expression.variables.VariableControllerImpl
import com.yandex.div.core.expression.variables.toVariable
import com.yandex.div.core.util.toLocalFunctions
import com.yandex.div.core.util.toVariables
import com.yandex.div.core.view2.Div2View
import com.yandex.div.core.view2.divs.DivActionBinder
import com.yandex.div.core.view2.errors.ErrorCollector
import com.yandex.div.data.Variable
import com.yandex.div.evaluable.EvaluationContext
import com.yandex.div.evaluable.Evaluator
import com.yandex.div.internal.Assert
import com.yandex.div.json.expressions.ExpressionResolver
import com.yandex.div2.Div
import com.yandex.div2.DivBase
import com.yandex.div2.DivFunction
import com.yandex.div2.DivTrigger
private const val ERROR_UNKNOWN_RESOLVER =
@@ -67,18 +65,33 @@ internal class RuntimeStore(
}
/**
* Returns runtime if it have been store before, otherwise creates new runtime using
* @param parentRuntime or @param parentResolver.
*
* NOTE: Always provide parentResolver or parentRuntime.
* Otherwise, if runtime wasn't created it will be created using rootRuntime
* Returns runtime if it have been stored before, otherwise creates new runtime using
* @param parentResolver
*/
internal fun getOrCreateRuntime(
path: String,
div: Div,
parentResolver: ExpressionResolver? = null,
parentRuntime: ExpressionsRuntime? = null,
) = tree.getNode(path)?.runtime ?: getRuntimeOrCreateChild(path, div, null, parentResolver, parentRuntime)
parentResolver: ExpressionResolver,
): ExpressionsRuntime? {
path.runtime?.let { return it }
val parentRuntime = getRuntimeWithOrNull(parentResolver)
val runtime = parentRuntime ?: rootRuntime ?: run {
reportError(ERROR_ROOT_RUNTIME_NOT_SPECIFIED)
return null
}
return getRuntimeOrCreateChild(path, div, runtime, parentRuntime)
}
/**
* Returns runtime if it have been stored before, otherwise creates new runtime using
* @param parentRuntime
*/
internal fun getOrCreateRuntime(path: String, div: Div, parentRuntime: ExpressionsRuntime) =
path.runtime ?: getRuntimeOrCreateChild(path, div, parentRuntime, parentRuntime)
private val String.runtime get() = tree.getNode(this)?.runtime
internal fun getRuntimeWithOrNull(resolver: ExpressionResolver) = resolverToRuntime[resolver]
@@ -103,7 +116,7 @@ internal class RuntimeStore(
path: String,
div: Div?,
resolver: ExpressionResolver,
parentResolver: ExpressionResolver?,
parentResolver: ExpressionResolver,
): ExpressionsRuntime? {
val runtimeForPath = tree.getNode(path)?.runtime
if (resolver == runtimeForPath?.expressionResolver) return runtimeForPath
@@ -114,7 +127,7 @@ internal class RuntimeStore(
}
runtimeForPath?.let { tree.removeRuntimeAndCleanup(divView, it, path) }
return getRuntimeOrCreateChild(path, div, existingRuntime, parentResolver)
return getRuntimeOrCreateChild(path, div, existingRuntime, getRuntimeWithOrNull(parentResolver))
}
internal fun cleanup(divView: DivViewFacade) {
@@ -137,15 +150,11 @@ internal class RuntimeStore(
baseRuntime: ExpressionsRuntime,
parentRuntime: ExpressionsRuntime?,
path: String,
variables: List<Variable>?,
variablesTriggers: List<DivTrigger>?,
functions: List<DivFunction>?,
div: DivBase,
): ExpressionsRuntime {
val localVariableController = VariableControllerImpl(baseRuntime.variableController)
if (!variables.isNullOrEmpty()) {
variables.forEach { localVariableController.declare(it) }
}
val functions = div.functions
var functionProvider = baseRuntime.functionProvider
if (!functions.isNullOrEmpty()) {
functionProvider += functions.toLocalFunctions()
@@ -168,50 +177,37 @@ internal class RuntimeStore(
onCreateCallback = onCreateCallback,
)
val triggerController = if (variablesTriggers.isNullOrEmpty()) {
null
} else {
TriggersController(
localVariableController,
resolver,
evaluator,
errorCollector,
div2Logger,
divActionBinder
).apply {
ensureTriggersSynced(variablesTriggers)
}
div.variables?.forEach {
localVariableController.declare(it.toVariable(resolver))
}
val triggerController = div.variableTriggers.toTriggersController(localVariableController, resolver, evaluator)
return ExpressionsRuntime(resolver, localVariableController, triggerController, functionProvider, this).also {
putRuntime(it, path, parentRuntime)
}
}
private fun List<DivTrigger>?.toTriggersController(
variableController: VariableController,
resolver: ExpressionResolver,
evaluator: Evaluator,
): TriggersController? {
if (isNullOrEmpty()) return null
val controller =
TriggersController(variableController, resolver, evaluator, errorCollector, div2Logger, divActionBinder)
controller.ensureTriggersSynced(this)
return controller
}
private fun getRuntimeOrCreateChild(
path: String,
div: Div?,
existingRuntime: ExpressionsRuntime? = null,
parentResolver: ExpressionResolver? = null,
parentRuntime: ExpressionsRuntime? = null,
): ExpressionsRuntime? {
val runtime = existingRuntime
?: parentRuntime
?: parentResolver?.let { getRuntimeWithOrNull(it) }
?: rootRuntime
?: run {
reportError(ERROR_ROOT_RUNTIME_NOT_SPECIFIED)
return null
}
val parentRuntime = parentRuntime ?: parentResolver?.let { getRuntimeWithOrNull(it) }
val variables = div?.value()?.variables?.toVariables()
val variableTriggers = div?.value()?.variableTriggers
val functions = div?.value()?.functions
if (needLocalRuntime(variables, variableTriggers, functions)) {
return createChildRuntime(runtime, parentRuntime, path, variables, variableTriggers, functions)
runtime: ExpressionsRuntime,
parentRuntime: ExpressionsRuntime?,
): ExpressionsRuntime {
if (div != null && div.needLocalRuntime) {
return createChildRuntime(runtime, parentRuntime, path, div.value())
}
tree.storeRuntime(runtime, parentRuntime, path)
@@ -1,15 +1,6 @@
package com.yandex.div.core.expression.local
import com.yandex.div.data.Variable
import com.yandex.div2.Div
import com.yandex.div2.DivFunction
import com.yandex.div2.DivTrigger
internal val Div.needLocalRuntime get() =
!value().run { variables.isNullOrEmpty() && variableTriggers.isNullOrEmpty() && functions.isNullOrEmpty() }
internal fun needLocalRuntime(
variables: List<Variable>?,
variableTriggers: List<DivTrigger>?,
functions: List<DivFunction>?
) = !(variables.isNullOrEmpty() && variableTriggers.isNullOrEmpty() && functions.isNullOrEmpty())
@@ -6,6 +6,7 @@ import com.yandex.div.data.Variable
import com.yandex.div.internal.parser.JsonParser
import com.yandex.div.json.ParsingErrorLogger
import com.yandex.div.json.ParsingException
import com.yandex.div.json.expressions.ExpressionResolver
import com.yandex.div2.DivVariable
import org.json.JSONArray
import org.json.JSONObject
@@ -16,7 +17,14 @@ object DivVariablesParser {
* @param variablesArray json-array of variables for parsing.
*/
@Throws(ParsingException::class)
fun parse(variablesArray: JSONArray, logger: ParsingErrorLogger): List<Variable> {
fun parse(variablesArray: JSONArray, logger: ParsingErrorLogger) =
parse(variablesArray, ExpressionResolver.EMPTY, logger)
/**
* @param variablesArray json-array of variables for parsing.
*/
@Throws(ParsingException::class)
fun parse(variablesArray: JSONArray, resolver: ExpressionResolver, logger: ParsingErrorLogger): List<Variable> {
val env = DivParsingEnvironment(logger)
val listValidator: (value: List<DivVariable>) -> Boolean = { true }
val key = "variables"
@@ -25,49 +33,49 @@ object DivVariablesParser {
}
val divVariables: List<DivVariable> = JsonParser.readList(
jsonObject, key, DivVariable.CREATOR, listValidator, logger, env)
return divVariables.map { it.toVariable() }
return divVariables.map { it.toVariable(resolver) }
}
}
internal fun DivVariable.toVariable(): Variable {
internal fun DivVariable.toVariable(resolver: ExpressionResolver): Variable {
return when (this) {
is DivVariable.Bool -> {
Variable.BooleanVariable(
this.value.name, this.value.value)
this.value.name, this.value.value.evaluate(resolver))
}
is DivVariable.Integer -> {
Variable.IntegerVariable(
this.value.name, this.value.value
this.value.name, this.value.value.evaluate(resolver)
)
}
is DivVariable.Number -> {
Variable.DoubleVariable(
this.value.name, this.value.value
this.value.name, this.value.value.evaluate(resolver)
)
}
is DivVariable.Str -> {
Variable.StringVariable(
this.value.name, this.value.value
this.value.name, this.value.value.evaluate(resolver)
)
}
is DivVariable.Color -> {
Variable.ColorVariable(
this.value.name, this.value.value
this.value.name, this.value.value.evaluate(resolver)
)
}
is DivVariable.Url -> {
Variable.UrlVariable(
this.value.name, this.value.value
this.value.name, this.value.value.evaluate(resolver)
)
}
is DivVariable.Dict -> {
Variable.DictVariable(
this.value.name, this.value.value
this.value.name, this.value.value.evaluate(resolver)
)
}
is DivVariable.Array -> {
Variable.ArrayVariable(
this.value.name, this.value.value
this.value.name, this.value.value.evaluate(resolver)
)
}
}
@@ -1,20 +1,15 @@
package com.yandex.div.core.util
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup
import android.view.animation.Interpolator
import android.view.animation.LinearInterpolator
import androidx.core.view.children
import com.yandex.div.core.animation.EaseInInterpolator
import com.yandex.div.core.animation.EaseInOutInterpolator
import com.yandex.div.core.animation.EaseInterpolator
import com.yandex.div.core.animation.EaseOutInterpolator
import com.yandex.div.core.animation.SpringInterpolator
import com.yandex.div.core.animation.reversed
import com.yandex.div.core.expression.variables.toVariable
import com.yandex.div.core.view2.divs.dpToPx
import com.yandex.div.data.Variable
import com.yandex.div.internal.core.buildItems
import com.yandex.div.internal.core.nonNullItems
import com.yandex.div.json.expressions.ExpressionResolver
@@ -38,7 +33,6 @@ import com.yandex.div2.DivState
import com.yandex.div2.DivSwitch
import com.yandex.div2.DivTabs
import com.yandex.div2.DivText
import com.yandex.div2.DivVariable
import com.yandex.div2.DivVideo
import java.util.Collections.min
@@ -113,13 +107,6 @@ internal val DivAnimationDirection.isAlternated: Boolean
}
}
internal fun requestHierarchyLayout(v : View) {
v.requestLayout()
if (v is ViewGroup) {
v.children.forEach { requestHierarchyLayout(it) }
}
}
internal fun DivBorder.getCornerRadii(
widthPx: Float,
heightPx: Float,
@@ -211,9 +198,3 @@ internal val Div.isBranch: Boolean
internal val Div.isLeaf: Boolean
get() = !isBranch
internal fun List<DivVariable>.toVariables(): List<Variable> {
return map {
it.toVariable()
}
}
@@ -13,6 +13,8 @@ import com.yandex.div.data.Variable
import com.yandex.div.evaluable.EvaluationContext
import com.yandex.div.evaluable.Evaluator
import com.yandex.div.internal.Assert
import com.yandex.div.json.expressions.Expression
import com.yandex.div.json.expressions.ExpressionResolver
import com.yandex.div2.Div
import com.yandex.div2.DivBase
import com.yandex.div2.DivVariable
@@ -82,7 +84,7 @@ class RuntimeStoreTest {
Assert.assertNotNull(underTest.getRuntimeWithOrNull(newResolver))
Assert.assertEquals(
runtimeFromCallback,
underTest.getOrCreateRuntime(path.fullPath, div)
underTest.getOrCreateRuntime(path.fullPath, div, newResolver)
)
}
@@ -91,7 +93,7 @@ class RuntimeStoreTest {
setVariable()
underTest.resolveRuntimeWith(divView, path.fullPath, div, resolver, resolver)
val runtime = underTest.getOrCreateRuntime(path.fullPath, div)
val runtime = underTest.getOrCreateRuntime(path.fullPath, div, resolver)
Assert.assertNotNull(underTest.getRuntimeWithOrNull(resolver))
Assert.assertNotNull(runtime)
Assert.assertEquals(
@@ -105,7 +107,7 @@ class RuntimeStoreTest {
val runtime = ExpressionsRuntime(resolver, mock(), null, functionProvider, underTest)
underTest.putRuntime(runtime, PATH, rootRuntime)
Assert.assertEquals(runtime, underTest.getOrCreateRuntime(path.fullPath, div))
Assert.assertEquals(runtime, underTest.getOrCreateRuntime(path.fullPath, div, resolver))
Assert.assertNotSame(runtime, rootRuntime)
Assert.assertNotNull(underTest.getRuntimeWithOrNull(resolver))
}
@@ -148,7 +150,7 @@ class RuntimeStoreTest {
@Test
fun `getOrCreateRuntime returns root runtime if parent runtime is not found`() {
val runtime = underTest.getOrCreateRuntime(path.fullPath, div)
val runtime = underTest.getOrCreateRuntime(path.fullPath, div, mock<ExpressionResolver>())
Assert.assertEquals(rootRuntime, runtime)
Assert.assertNotNull(underTest.getRuntimeWithOrNull(resolver))
}
@@ -165,7 +167,7 @@ class RuntimeStoreTest {
)
Assert.assertEquals(
runtimeFromCallback,
underTest.getOrCreateRuntime(path.fullPath, div)
underTest.getOrCreateRuntime(path.fullPath, div, resolver)
)
}
@@ -180,7 +182,7 @@ class RuntimeStoreTest {
)
underTest.resolveRuntimeWith(divView, path.fullPath, div, resolver, resolver)
val newRuntime = underTest.getOrCreateRuntime(path.fullPath, div)
val newRuntime = underTest.getOrCreateRuntime(path.fullPath, div, resolver)
Assert.assertNotNull(newRuntime)
Assert.assertEquals(
@@ -194,7 +196,7 @@ class RuntimeStoreTest {
}
private fun setVariable() {
val variables = listOf(DivVariable.Integer(IntegerVariable(CHILD_VARIABLE, 123)))
val variables = listOf(DivVariable.Integer(IntegerVariable(CHILD_VARIABLE, Expression.constant(123))))
whenever(divBase.variables).doReturn(variables)
}
}
@@ -125,8 +125,8 @@ class LocalVariablesTest {
}
private fun setVariable(name: String, value: String, path: String) {
val variableController = div2View.expressionsRuntime
?.runtimeStore?.getOrCreateRuntime(path, div)?.variableController
val runtime = div2View.expressionsRuntime
val variableController = runtime?.runtimeStore?.getOrCreateRuntime(path, div, runtime)?.variableController
val variable = variableController?.getMutableVariable(name) ?: return
variable.set(value)
}
@@ -9,6 +9,7 @@ import com.yandex.div.core.expression.ExpressionTestCaseUtils
import com.yandex.div.core.expression.ExpressionTestCaseUtils.VALUE_TYPE_ARRAY
import com.yandex.div.core.expression.ExpressionTestCaseUtils.VALUE_TYPE_DICT
import com.yandex.div.core.expression.ExpressionTestCaseUtils.createVariable
import com.yandex.div.core.expression.variables.wrapVariableValue
import com.yandex.div.core.images.DivImageDownloadCallback
import com.yandex.div.core.images.DivImageLoader
import com.yandex.div.core.images.LoadReference
@@ -56,8 +57,8 @@ class IntegrationMultiplatformTest(testCase: TestCaseOrError<IntegrationTestCase
)
}
is IntegrationTestCase.ExpectedResult.Variable -> {
val expectedValue = it.value
val actualValue = variableController.get(it.name)?.getValue()
val expectedValue = it.value.wrapVariableValue()
val actualValue = divView.expressionsRuntime?.variableController?.get(it.name)
if (it.type == VALUE_TYPE_DICT || it.type == VALUE_TYPE_ARRAY) {
Assert.assertEquals(expectedValue.toString(), actualValue.toString())
} else {
-9
View File
@@ -16,7 +16,6 @@
]
},
"value": {
"supports_expressions": false,
"type": "string",
"$description": "translations.json#/div_variable_value"
}
@@ -42,7 +41,6 @@
]
},
"value": {
"supports_expressions": false,
"long_type": true,
"type": "integer",
"$description": "translations.json#/div_variable_value"
@@ -69,7 +67,6 @@
]
},
"value": {
"supports_expressions": false,
"type": "number",
"$description": "translations.json#/div_variable_value"
}
@@ -96,7 +93,6 @@
]
},
"value": {
"supports_expressions": false,
"$ref": "common.json#/boolean_int",
"$description": "translations.json#/div_variable_value"
}
@@ -116,14 +112,12 @@
"$description": "translations.json#/div_variable_name"
},
"type": {
"supports_expressions": false,
"type": "string",
"enum": [
"color"
]
},
"value": {
"supports_expressions": false,
"$ref": "common.json#/color",
"$description": "translations.json#/div_variable_value"
}
@@ -149,7 +143,6 @@
]
},
"value": {
"supports_expressions": false,
"$ref": "common.json#/url",
"$description": "translations.json#/div_variable_value"
}
@@ -175,7 +168,6 @@
]
},
"value": {
"supports_expressions": false,
"type": "dict",
"$description": "translations.json#/div_variable_value"
}
@@ -202,7 +194,6 @@
},
"value": {
"type": "array",
"supports_expressions": false,
"$description": "translations.json#/div_variable_value"
}
},
@@ -68,6 +68,7 @@
}
],
"platforms": [
"android",
"web"
]
}
@@ -34,6 +34,7 @@
}
],
"platforms": [
"android",
"web"
]
}
@@ -39,6 +39,7 @@
}
],
"platforms": [
"android",
"web"
]
}