去掉circle

This commit is contained in:
zoujiandong 2024-01-12 17:19:53 +08:00
parent 360c1bb977
commit 562d4e9691
17 changed files with 1 additions and 715 deletions

View File

@ -1,4 +0,0 @@
/// <reference types="miniprogram-api-typings" />
declare type CanvasContext = WechatMiniprogram.CanvasContext;
export declare function adaptor(ctx: CanvasContext & Record<string, unknown>): CanvasContext;
export {};

View File

@ -1,47 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.adaptor = void 0;
function adaptor(ctx) {
// @ts-ignore
return Object.assign(ctx, {
setStrokeStyle: function (val) {
ctx.strokeStyle = val;
},
setLineWidth: function (val) {
ctx.lineWidth = val;
},
setLineCap: function (val) {
ctx.lineCap = val;
},
setFillStyle: function (val) {
ctx.fillStyle = val;
},
setFontSize: function (val) {
ctx.font = String(val);
},
setGlobalAlpha: function (val) {
ctx.globalAlpha = val;
},
setLineJoin: function (val) {
ctx.lineJoin = val;
},
setTextAlign: function (val) {
ctx.textAlign = val;
},
setMiterLimit: function (val) {
ctx.miterLimit = val;
},
setShadow: function (offsetX, offsetY, blur, color) {
ctx.shadowOffsetX = offsetX;
ctx.shadowOffsetY = offsetY;
ctx.shadowBlur = blur;
ctx.shadowColor = color;
},
setTextBaseline: function (val) {
ctx.textBaseline = val;
},
createCircularGradient: function () { },
draw: function () { },
});
}
exports.adaptor = adaptor;

View File

@ -1 +0,0 @@
export {};

View File

@ -1,203 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var color_1 = require("../common/color");
var component_1 = require("../common/component");
var utils_1 = require("../common/utils");
var validator_1 = require("../common/validator");
var version_1 = require("../common/version");
var canvas_1 = require("./canvas");
function format(rate) {
return Math.min(Math.max(rate, 0), 100);
}
var PERIMETER = 2 * Math.PI;
var BEGIN_ANGLE = -Math.PI / 2;
var STEP = 1;
(0, component_1.VantComponent)({
props: {
text: String,
lineCap: {
type: String,
value: 'round',
},
value: {
type: Number,
value: 0,
observer: 'reRender',
},
speed: {
type: Number,
value: 50,
},
size: {
type: Number,
value: 100,
observer: function () {
this.drawCircle(this.currentValue);
},
},
fill: String,
layerColor: {
type: String,
value: color_1.WHITE,
},
color: {
type: null,
value: color_1.BLUE,
observer: function () {
var _this = this;
this.setHoverColor().then(function () {
_this.drawCircle(_this.currentValue);
});
},
},
type: {
type: String,
value: '',
},
strokeWidth: {
type: Number,
value: 4,
},
clockwise: {
type: Boolean,
value: true,
},
},
data: {
hoverColor: color_1.BLUE,
},
methods: {
getContext: function () {
var _this = this;
var _a = this.data, type = _a.type, size = _a.size;
if (type === '' || !(0, version_1.canIUseCanvas2d)()) {
var ctx = wx.createCanvasContext('van-circle', this);
return Promise.resolve(ctx);
}
var dpr = (0, utils_1.getSystemInfoSync)().pixelRatio;
return new Promise(function (resolve) {
wx.createSelectorQuery()
.in(_this)
.select('#van-circle')
.node()
.exec(function (res) {
var canvas = res[0].node;
var ctx = canvas.getContext(type);
if (!_this.inited) {
_this.inited = true;
canvas.width = size * dpr;
canvas.height = size * dpr;
ctx.scale(dpr, dpr);
}
resolve((0, canvas_1.adaptor)(ctx));
});
});
},
setHoverColor: function () {
var _this = this;
var _a = this.data, color = _a.color, size = _a.size;
if ((0, validator_1.isObj)(color)) {
return this.getContext().then(function (context) {
var LinearColor = context.createLinearGradient(size, 0, 0, 0);
Object.keys(color)
.sort(function (a, b) { return parseFloat(a) - parseFloat(b); })
.map(function (key) {
return LinearColor.addColorStop(parseFloat(key) / 100, color[key]);
});
_this.hoverColor = LinearColor;
});
}
this.hoverColor = color;
return Promise.resolve();
},
presetCanvas: function (context, strokeStyle, beginAngle, endAngle, fill) {
var _a = this.data, strokeWidth = _a.strokeWidth, lineCap = _a.lineCap, clockwise = _a.clockwise, size = _a.size;
var position = size / 2;
var radius = position - strokeWidth / 2;
context.setStrokeStyle(strokeStyle);
context.setLineWidth(strokeWidth);
context.setLineCap(lineCap);
context.beginPath();
context.arc(position, position, radius, beginAngle, endAngle, !clockwise);
context.stroke();
if (fill) {
context.setFillStyle(fill);
context.fill();
}
},
renderLayerCircle: function (context) {
var _a = this.data, layerColor = _a.layerColor, fill = _a.fill;
this.presetCanvas(context, layerColor, 0, PERIMETER, fill);
},
renderHoverCircle: function (context, formatValue) {
var clockwise = this.data.clockwise;
// 结束角度
var progress = PERIMETER * (formatValue / 100);
var endAngle = clockwise
? BEGIN_ANGLE + progress
: 3 * Math.PI - (BEGIN_ANGLE + progress);
this.presetCanvas(context, this.hoverColor, BEGIN_ANGLE, endAngle);
},
drawCircle: function (currentValue) {
var _this = this;
var size = this.data.size;
this.getContext().then(function (context) {
context.clearRect(0, 0, size, size);
_this.renderLayerCircle(context);
var formatValue = format(currentValue);
if (formatValue !== 0) {
_this.renderHoverCircle(context, formatValue);
}
context.draw();
});
},
reRender: function () {
var _this = this;
// tofector 动画暂时没有想到好的解决方案
var _a = this.data, value = _a.value, speed = _a.speed;
if (speed <= 0 || speed > 1000) {
this.drawCircle(value);
return;
}
this.clearMockInterval();
this.currentValue = this.currentValue || 0;
var run = function () {
_this.interval = setTimeout(function () {
if (_this.currentValue !== value) {
if (Math.abs(_this.currentValue - value) < STEP) {
_this.currentValue = value;
}
else if (_this.currentValue < value) {
_this.currentValue += STEP;
}
else {
_this.currentValue -= STEP;
}
_this.drawCircle(_this.currentValue);
run();
}
else {
_this.clearMockInterval();
}
}, 1000 / speed);
};
run();
},
clearMockInterval: function () {
if (this.interval) {
clearTimeout(this.interval);
this.interval = null;
}
},
},
mounted: function () {
var _this = this;
this.currentValue = this.data.value;
this.setHoverColor().then(function () {
_this.drawCircle(_this.currentValue);
});
},
destroyed: function () {
this.clearMockInterval();
},
});

View File

@ -1,3 +0,0 @@
{
"component": true
}

View File

@ -1,9 +0,0 @@
<wxs src="../wxs/utils.wxs" module="utils" />
<view class="van-circle">
<canvas class="van-circle__canvas" type="{{ type }}" style="width: {{ utils.addUnit(size) }};height:{{ utils.addUnit(size) }}" id="van-circle" canvas-id="van-circle"></canvas>
<view wx:if="{{ !text }}" class="van-circle__text">
<slot></slot>
</view>
<cover-view wx:else class="van-circle__text">{{ text }}</cover-view>
</view>

View File

@ -1 +0,0 @@
@import '../common/index.wxss';.van-circle{display:inline-block;position:relative;text-align:center}.van-circle__text{color:var(--circle-text-color,#323233);left:0;position:absolute;top:50%;transform:translateY(-50%);width:100%}

View File

@ -1,28 +0,0 @@
import { SuperComponent } from '../common/src/index';
export default class CheckTag extends SuperComponent {
data: {
prefix: string;
classPrefix: string;
className: string;
};
properties: import("./type").TdCheckTagProps;
externalClasses: string[];
controlledProps: {
key: string;
event: string;
}[];
options: {
multipleSlots: boolean;
};
lifetimes: {
attached(): void;
};
observers: {
'size, disabled, checked'(): void;
icon(v: any): void;
};
methods: {
setClass(): void;
onClick(): void;
};
}

View File

@ -1,78 +0,0 @@
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { wxComponent, SuperComponent } from '../common/src/index';
import config from '../common/config';
import props from './props';
import { classNames, calcIcon } from '../common/utils';
const { prefix } = config;
const name = `${prefix}-tag`;
let CheckTag = class CheckTag extends SuperComponent {
constructor() {
super(...arguments);
this.data = {
prefix,
classPrefix: name,
className: '',
};
this.properties = props;
this.externalClasses = [`${prefix}-class`];
this.controlledProps = [
{
key: 'checked',
event: 'change',
},
];
this.options = {
multipleSlots: true,
};
this.lifetimes = {
attached() {
this.setClass();
},
};
this.observers = {
'size, disabled, checked'() {
this.setClass();
},
icon(v) {
this.setData({
_icon: calcIcon(v),
});
},
};
this.methods = {
setClass() {
const { classPrefix } = this.data;
const { size, variant, disabled, checked } = this.properties;
const tagClass = [
classPrefix,
`${classPrefix}--checkable`,
disabled ? `${classPrefix}--disabled` : '',
checked ? `${classPrefix}--checked` : '',
`${classPrefix}--${checked ? 'primary' : 'default'}`,
`${classPrefix}--${size}`,
`${classPrefix}--${variant}`,
];
const className = classNames(tagClass);
this.setData({
className,
});
},
onClick() {
if (this.data.disabled)
return;
const { checked } = this.data;
this._trigger('click');
this._trigger('change', { checked: !checked });
},
};
}
};
CheckTag = __decorate([
wxComponent()
], CheckTag);
export default CheckTag;

View File

@ -1,6 +0,0 @@
{
"component": true,
"usingComponents": {
"t-icon": "../icon/icon"
}
}

View File

@ -1,15 +0,0 @@
<wxs src="../common/utils.wxs" module="_" />
<import src="../common/template/icon.wxml" />
<view style="{{ customStyle }}" class="{{className}} {{prefix}}-class" bind:tap="onClick">
<view aria-hidden="{{true}}" class="{{classPrefix}}__icon">
<template wx:if="{{_icon}}" is="icon" data="{{ tClass: prefix + '-icon', ..._icon }}" />
<slot name="icon" />
</view>
<view class="{{classPrefix}}__text">
<slot />
<slot name="content" />
<block wx:if="{{_.isArray(content) && content.length == 2}}">{{checked ? content[0] : content[1]}}</block>
<block wx:else>{{content}}</block>
</view>
</view>

View File

@ -1,224 +0,0 @@
.t-float-left {
float: left;
}
.t-float-right {
float: right;
}
@keyframes tdesign-fade-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.hotspot-expanded.relative {
position: relative;
}
.hotspot-expanded::after {
content: '';
display: block;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
transform: scale(1.5);
}
.t-tag {
display: inline-flex;
align-items: center;
border: 2rpx solid transparent;
box-sizing: border-box;
border-radius: var(--td-tag-square-border-radius, 8rpx);
font-size: var(--td-tag-medium-font-size, var(--td-font-size-s, 24rpx));
user-select: none;
vertical-align: middle;
}
.t-tag__text {
word-wrap: normal;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.t-tag__icon {
display: flex;
align-items: center;
}
.t-tag__icon:not(:empty) + .t-tag__text:not(:empty) {
margin-left: 8rpx;
}
.t-tag--small {
height: var(--td-tag-small-height, 40rpx);
line-height: var(--td-tag-small-height, 40rpx);
padding: 0 var(--td-tag-small-padding, 11rpx);
font-size: var(--td-tag-small-font-size, var(--td-font-size, 20rpx));
}
.t-tag--small .t-icon,
.t-tag--small .t-icon-close {
font-size: var(--td-tag-small-icon-size, 24rpx);
}
.t-tag--medium {
height: var(--td-tag-medium-height, 48rpx);
line-height: var(--td-tag-medium-height, 48rpx);
padding: 0 var(--td-tag-medium-padding, 15rpx);
font-size: var(--td-tag-medium-font-size, var(--td-font-size-s, 24rpx));
}
.t-tag--medium .t-icon,
.t-tag--medium .t-icon-close {
font-size: var(--td-tag-medium-icon-size, 28rpx);
}
.t-tag--large {
height: var(--td-tag-large-height, 56rpx);
line-height: var(--td-tag-large-height, 56rpx);
padding: 0 var(--td-tag-large-padding, 15rpx);
font-size: var(--td-tag-large-font-size, var(--td-font-size-base, 28rpx));
}
.t-tag--large .t-icon,
.t-tag--large .t-icon-close {
font-size: var(--td-tag-large-icon-size, 32rpx);
}
.t-tag--extra-large {
height: var(--td-tag-extra-large-height, 80rpx);
line-height: var(--td-tag-extra-large-height, 80rpx);
padding: 0 var(--td-tag-extra-large-padding, 31rpx);
font-size: var(--td-tag-extra-large-font-size, var(--td-font-size-base, 28rpx));
}
.t-tag--extra-large .t-icon,
.t-tag--extra-large .t-icon-close {
font-size: var(--td-tag-extra-large-icon-size, 32rpx);
}
.t-tag--dark.t-tag--default {
color: var(--td-white-color-1, #fff);
border-color: var(--td-tag-default-color, var(--td-gray-color-3, #e7e7e7));
background-color: var(--td-tag-default-color, var(--td-gray-color-3, #e7e7e7));
}
.t-tag--dark.t-tag--primary {
color: var(--td-white-color-1, #fff);
border-color: var(--td-tag-primary-color, var(--td-primary-color, #0052d9));
background-color: var(--td-tag-primary-color, var(--td-primary-color, #0052d9));
}
.t-tag--dark.t-tag--success {
color: var(--td-white-color-1, #fff);
border-color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #00a870)));
background-color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #00a870)));
}
.t-tag--dark.t-tag--warning {
color: var(--td-white-color-1, #fff);
border-color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #ed7b2f)));
background-color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #ed7b2f)));
}
.t-tag--dark.t-tag--danger {
color: var(--td-white-color-1, #fff);
border-color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #e34d59)));
background-color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #e34d59)));
}
.t-tag--dark.t-tag--default {
color: var(--td-tag-default-font-color, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)));
}
.t-tag--outline.t-tag--default {
color: var(--td-tag-default-color, var(--td-gray-color-3, #e7e7e7));
border-color: var(--td-tag-default-color, var(--td-gray-color-3, #e7e7e7));
background-color: var(--td-tag-default-light-color, var(--td-gray-color-1, #f3f3f3));
}
.t-tag--outline.t-tag--primary {
color: var(--td-tag-primary-color, var(--td-primary-color, #0052d9));
border-color: var(--td-tag-primary-color, var(--td-primary-color, #0052d9));
background-color: var(--td-tag-primary-light-color, var(--td-primary-color-1, #ecf2fe));
}
.t-tag--outline.t-tag--success {
color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #00a870)));
border-color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #00a870)));
background-color: var(--td-tag-success-light-color, var(--td-success-color-1, #e8f8f2));
}
.t-tag--outline.t-tag--warning {
color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #ed7b2f)));
border-color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #ed7b2f)));
background-color: var(--td-tag-warning-light-color, var(--td-warning-color-1, #fef3e6));
}
.t-tag--outline.t-tag--danger {
color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #e34d59)));
border-color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #e34d59)));
background-color: var(--td-tag-danger-light-color, var(--td-error-color-1, #fdecee));
}
.t-tag--outline.t-tag--default {
color: var(--td-tag-default-font-color, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)));
}
.t-tag--outline.t-tag--default {
background-color: var(--td-tag-outline-bg-color, var(--td-bg-color-block, #fff));
}
.t-tag--outline.t-tag--primary {
background-color: var(--td-tag-outline-bg-color, var(--td-bg-color-block, #fff));
}
.t-tag--outline.t-tag--success {
background-color: var(--td-tag-outline-bg-color, var(--td-bg-color-block, #fff));
}
.t-tag--outline.t-tag--warning {
background-color: var(--td-tag-outline-bg-color, var(--td-bg-color-block, #fff));
}
.t-tag--outline.t-tag--danger {
background-color: var(--td-tag-outline-bg-color, var(--td-bg-color-block, #fff));
}
.t-tag--light.t-tag--default {
color: var(--td-tag-default-color, var(--td-gray-color-3, #e7e7e7));
border-color: var(--td-tag-default-light-color, var(--td-gray-color-1, #f3f3f3));
background-color: var(--td-tag-default-light-color, var(--td-gray-color-1, #f3f3f3));
}
.t-tag--light.t-tag--primary {
color: var(--td-tag-primary-color, var(--td-primary-color, #0052d9));
border-color: var(--td-tag-primary-light-color, var(--td-primary-color-1, #ecf2fe));
background-color: var(--td-tag-primary-light-color, var(--td-primary-color-1, #ecf2fe));
}
.t-tag--light.t-tag--success {
color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #00a870)));
border-color: var(--td-tag-success-light-color, var(--td-success-color-1, #e8f8f2));
background-color: var(--td-tag-success-light-color, var(--td-success-color-1, #e8f8f2));
}
.t-tag--light.t-tag--warning {
color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #ed7b2f)));
border-color: var(--td-tag-warning-light-color, var(--td-warning-color-1, #fef3e6));
background-color: var(--td-tag-warning-light-color, var(--td-warning-color-1, #fef3e6));
}
.t-tag--light.t-tag--danger {
color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #e34d59)));
border-color: var(--td-tag-danger-light-color, var(--td-error-color-1, #fdecee));
background-color: var(--td-tag-danger-light-color, var(--td-error-color-1, #fdecee));
}
.t-tag--light.t-tag--default {
color: var(--td-tag-default-font-color, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)));
}
.t-tag--light-outline.t-tag--default {
color: var(--td-tag-default-color, var(--td-gray-color-3, #e7e7e7));
border-color: var(--td-tag-default-color, var(--td-gray-color-3, #e7e7e7));
background-color: var(--td-tag-default-light-color, var(--td-gray-color-1, #f3f3f3));
}
.t-tag--light-outline.t-tag--primary {
color: var(--td-tag-primary-color, var(--td-primary-color, #0052d9));
border-color: var(--td-tag-primary-color, var(--td-primary-color, #0052d9));
background-color: var(--td-tag-primary-light-color, var(--td-primary-color-1, #ecf2fe));
}
.t-tag--light-outline.t-tag--success {
color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #00a870)));
border-color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #00a870)));
background-color: var(--td-tag-success-light-color, var(--td-success-color-1, #e8f8f2));
}
.t-tag--light-outline.t-tag--warning {
color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #ed7b2f)));
border-color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #ed7b2f)));
background-color: var(--td-tag-warning-light-color, var(--td-warning-color-1, #fef3e6));
}
.t-tag--light-outline.t-tag--danger {
color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #e34d59)));
border-color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #e34d59)));
background-color: var(--td-tag-danger-light-color, var(--td-error-color-1, #fdecee));
}
.t-tag--light-outline.t-tag--default {
color: var(--td-tag-default-font-color, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)));
border-color: var(--td-gray-color-4, #dcdcdc);
}
.t-tag.t-tag--closable.t-tag--disabled {
cursor: not-allowed;
color: var(--td-tag-disabled-color, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)));
background-color: var(--td-tag-disabled-background-color, var(--td-gray-color-2, #eeeeee));
border-color: var(--td-tag-disabled-border-color, var(--td-gray-color-4, #dcdcdc));
}

View File

@ -1,3 +0,0 @@
import { TdCheckTagProps } from './type';
declare const props: TdCheckTagProps;
export default props;

View File

@ -1,44 +0,0 @@
const props = {
checked: {
type: null,
value: undefined,
},
defaultChecked: {
type: null,
value: undefined,
},
closable: {
type: Boolean,
value: false,
},
content: {
type: null,
},
customStyle: {
type: String,
value: '',
},
disabled: {
type: Boolean,
value: false,
},
externalClasses: {
type: Array,
},
icon: {
type: null,
},
shape: {
type: String,
value: 'square',
},
size: {
type: String,
value: 'medium',
},
variant: {
type: String,
value: 'dark',
},
};
export default props;

View File

@ -1,47 +0,0 @@
import { SizeEnum } from '../common/common';
export interface TdCheckTagProps {
checked?: {
type: BooleanConstructor;
value?: boolean;
};
defaultChecked?: {
type: BooleanConstructor;
value?: boolean;
};
closable?: {
type: BooleanConstructor;
value?: boolean;
};
content?: {
type: null;
value?: string | number | string[];
};
customStyle?: {
type: StringConstructor;
value?: string;
};
disabled?: {
type: BooleanConstructor;
value?: boolean;
};
externalClasses?: {
type: ArrayConstructor;
value?: ['t-class'];
};
icon?: {
type: null;
value?: string | object;
};
shape?: {
type: StringConstructor;
value?: 'square' | 'round' | 'mark';
};
size?: {
type: StringConstructor;
value?: SizeEnum;
};
variant?: {
type: StringConstructor;
value?: 'dark' | 'light' | 'outline' | 'light-outline';
};
}

View File

@ -1 +0,0 @@
export {};

View File

@ -3,7 +3,7 @@
"setting": { "setting": {
"compileHotReLoad": true, "compileHotReLoad": true,
"urlCheck": false, "urlCheck": false,
"bigPackageSizeSupport": true "bigPackageSizeSupport": false
}, },
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"condition": { "condition": {